[Jython-checkins] jython (merge default -> default): Merged

jim.baker jython-checkins at python.org
Tue Mar 19 01:13:10 CET 2013


http://hg.python.org/jython/rev/81c754c55d41
changeset:   7087:81c754c55d41
parent:      7086:9527e08af81d
parent:      7085:039dba919d92
user:        Jim Baker <jbaker at zyasoft.com>
date:        Mon Mar 18 16:53:18 2013 -0700
summary:
  Merged

files:
  .hgtags                                                                         |     2 +
  ACKNOWLEDGMENTS                                                                 |     4 +
  Lib/_jyio.py                                                                    |   132 +-
  Lib/_warnings.py                                                                |   388 ---
  Lib/decimal.py                                                                  |     8 +-
  Lib/doctest.py                                                                  |    17 +-
  Lib/platform.py                                                                 |   162 +-
  Lib/socket.py                                                                   |    71 +-
  Lib/test/string_tests.py                                                        |     8 +-
  Lib/test/test_charmapcodec.py                                                   |    61 +
  Lib/test/test_codecs.py                                                         |     2 +-
  Lib/test/test_collections.py                                                    |     7 +-
  Lib/test/test_csv.py                                                            |  1072 ----------
  Lib/test/test_decimal.py                                                        |    12 +
  Lib/test/test_dict_jy.py                                                        |     7 +
  Lib/test/test_httpservers.py                                                    |    76 +-
  Lib/test/test_io.py                                                             |    49 +-
  Lib/test/test_io_jy.py                                                          |    66 -
  Lib/test/test_mailbox.py                                                        |   208 +-
  Lib/test/test_memoryio.py                                                       |   770 +++++++
  Lib/test/test_platform.py                                                       |     2 +
  Lib/test/test_site.py                                                           |     1 -
  Lib/test/test_support.py                                                        |   158 +-
  Lib/test/test_threading_jy.py                                                   |     9 +
  Lib/test/test_urllib2.py                                                        |     6 +-
  Lib/test/test_warnings.py                                                       |    11 +-
  Lib/test/test_zlib.py                                                           |    93 +-
  Lib/zlib.py                                                                     |    19 +-
  NEWS                                                                            |    29 +-
  README.txt                                                                      |    22 +-
  build.xml                                                                       |   129 +-
  extlibs/svnant-jars/svnClientAdapter.jar                                        |   Bin 
  extlibs/svnant-jars/svnant.jar                                                  |   Bin 
  extlibs/svnant-jars/svnjavahl.jar                                               |   Bin 
  installer/src/java/org/apache/LICENSE.txt                                       |   202 +
  installer/src/java/org/apache/commons/cli/AlreadySelectedException.java         |    81 +
  installer/src/java/org/apache/commons/cli/BasicParser.java                      |    92 +
  installer/src/java/org/apache/commons/cli/CommandLine.java                      |   328 +++
  installer/src/java/org/apache/commons/cli/CommandLineParser.java                |    97 +
  installer/src/java/org/apache/commons/cli/GnuParser.java                        |   187 +
  installer/src/java/org/apache/commons/cli/HelpFormatter.java                    |   542 +++++
  installer/src/java/org/apache/commons/cli/MissingArgumentException.java         |    82 +
  installer/src/java/org/apache/commons/cli/MissingOptionException.java           |    81 +
  installer/src/java/org/apache/commons/cli/Option.java                           |   575 +++++
  installer/src/java/org/apache/commons/cli/OptionBuilder.java                    |   368 +++
  installer/src/java/org/apache/commons/cli/OptionGroup.java                      |   187 +
  installer/src/java/org/apache/commons/cli/Options.java                          |   331 +++
  installer/src/java/org/apache/commons/cli/ParseException.java                   |    82 +
  installer/src/java/org/apache/commons/cli/Parser.java                           |   282 ++
  installer/src/java/org/apache/commons/cli/PatternOptionBuilder.java             |   204 +
  installer/src/java/org/apache/commons/cli/PosixParser.java                      |   342 +++
  installer/src/java/org/apache/commons/cli/TypeHandler.java                      |   252 ++
  installer/src/java/org/apache/commons/cli/UnrecognizedOptionException.java      |    82 +
  installer/src/java/org/python/util/install/AbstractWizard.java                  |   438 ++++
  installer/src/java/org/python/util/install/AbstractWizardHeader.java            |    12 +
  installer/src/java/org/python/util/install/AbstractWizardPage.java              |   153 +
  installer/src/java/org/python/util/install/AbstractWizardValidator.java         |   132 +
  installer/src/java/org/python/util/install/ChildProcess.java                    |   357 +++
  installer/src/java/org/python/util/install/ConsoleInstaller.java                |   609 +++++
  installer/src/java/org/python/util/install/DirectoryFilter.java                 |    23 +
  installer/src/java/org/python/util/install/DirectorySelectionPage.java          |   200 +
  installer/src/java/org/python/util/install/DirectorySelectionPageValidator.java |    36 +
  installer/src/java/org/python/util/install/EmptyValidator.java                  |     8 +
  installer/src/java/org/python/util/install/FileHelper.java                      |   208 +
  installer/src/java/org/python/util/install/FrameInstaller.java                  |   186 +
  installer/src/java/org/python/util/install/Installation.java                    |   439 ++++
  installer/src/java/org/python/util/install/InstallationCancelledException.java  |     9 +
  installer/src/java/org/python/util/install/InstallationListener.java            |     7 +
  installer/src/java/org/python/util/install/InstallationType.java                |   120 +
  installer/src/java/org/python/util/install/InstallerCommandLine.java            |   456 ++++
  installer/src/java/org/python/util/install/InstallerException.java              |    21 +
  installer/src/java/org/python/util/install/JarInfo.java                         |   235 ++
  installer/src/java/org/python/util/install/JarInstaller.java                    |   283 ++
  installer/src/java/org/python/util/install/JavaHomeHandler.java                 |   209 +
  installer/src/java/org/python/util/install/JavaSelectionPage.java               |   199 +
  installer/src/java/org/python/util/install/JavaSelectionPageValidator.java      |    31 +
  installer/src/java/org/python/util/install/JavaVersionTester.java               |    61 +
  installer/src/java/org/python/util/install/LanguagePage.java                    |   121 +
  installer/src/java/org/python/util/install/LicensePage.java                     |   120 +
  installer/src/java/org/python/util/install/LicensePageValidator.java            |    17 +
  installer/src/java/org/python/util/install/OverviewPage.java                    |   243 ++
  installer/src/java/org/python/util/install/ProgressListener.java                |    15 +
  installer/src/java/org/python/util/install/ProgressPage.java                    |   122 +
  installer/src/java/org/python/util/install/ReadmePage.java                      |    75 +
  installer/src/java/org/python/util/install/StandalonePackager.java              |   184 +
  installer/src/java/org/python/util/install/StartScriptGenerator.java            |   244 ++
  installer/src/java/org/python/util/install/SuccessPage.java                     |    55 +
  installer/src/java/org/python/util/install/TextConstants.java                   |   148 +
  installer/src/java/org/python/util/install/TextConstants_de.java                |   148 +
  installer/src/java/org/python/util/install/TextConstants_en.java                |     5 +
  installer/src/java/org/python/util/install/TextKeys.java                        |   135 +
  installer/src/java/org/python/util/install/TypePage.java                        |   268 ++
  installer/src/java/org/python/util/install/UnicodeSequences.java                |    19 +
  installer/src/java/org/python/util/install/ValidationEvent.java                 |    14 +
  installer/src/java/org/python/util/install/ValidationException.java             |    19 +
  installer/src/java/org/python/util/install/ValidationInformationException.java  |    21 +
  installer/src/java/org/python/util/install/ValidationListener.java              |    11 +
  installer/src/java/org/python/util/install/Wizard.java                          |    90 +
  installer/src/java/org/python/util/install/WizardEvent.java                     |    13 +
  installer/src/java/org/python/util/install/WizardHeader.java                    |    89 +
  installer/src/java/org/python/util/install/WizardListener.java                  |    13 +
  installer/src/java/org/python/util/install/driver/Autotest.java                 |   274 ++
  installer/src/java/org/python/util/install/driver/ConsoleAutotest.java          |    30 +
  installer/src/java/org/python/util/install/driver/ConsoleDriver.java            |    58 +
  installer/src/java/org/python/util/install/driver/DriverException.java          |    17 +
  installer/src/java/org/python/util/install/driver/GuiAutotest.java              |   188 +
  installer/src/java/org/python/util/install/driver/InstallationDriver.java       |   416 +++
  installer/src/java/org/python/util/install/driver/NormalVerifier.java           |   287 ++
  installer/src/java/org/python/util/install/driver/SilentAutotest.java           |    25 +
  installer/src/java/org/python/util/install/driver/StandaloneVerifier.java       |    74 +
  installer/src/java/org/python/util/install/driver/Tunnel.java                   |    61 +
  installer/src/java/org/python/util/install/driver/Verifier.java                 |    17 +
  installer/src/java/org/python/util/install/driver/jython_test.bat.template      |    73 +
  installer/src/java/org/python/util/install/driver/jython_test.template          |    58 +
  installer/src/java/org/python/util/install/jython_small_c.png                   |   Bin 
  installer/test/java/org/AllTests.java                                           |   101 +
  installer/test/java/org/apache/commons/cli/ApplicationTest.java                 |   120 +
  installer/test/java/org/apache/commons/cli/BugsTest.java                        |   348 +++
  installer/test/java/org/apache/commons/cli/BuildTest.java                       |    96 +
  installer/test/java/org/apache/commons/cli/GnuParseTest.java                    |   265 ++
  installer/test/java/org/apache/commons/cli/HelpFormatterExamples.java           |   106 +
  installer/test/java/org/apache/commons/cli/HelpFormatterTest.java               |    82 +
  installer/test/java/org/apache/commons/cli/OptionBuilderTest.java               |   160 +
  installer/test/java/org/apache/commons/cli/OptionGroupSortTest.java             |    43 +
  installer/test/java/org/apache/commons/cli/OptionGroupTest.java                 |   246 ++
  installer/test/java/org/apache/commons/cli/OptionsTest.java                     |    28 +
  installer/test/java/org/apache/commons/cli/ParseRequiredTest.java               |   113 +
  installer/test/java/org/apache/commons/cli/ParseTest.java                       |   285 ++
  installer/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java        |    82 +
  installer/test/java/org/apache/commons/cli/PosixParserTest.java                 |   138 +
  installer/test/java/org/apache/commons/cli/TestHelpFormatter.java               |   170 +
  installer/test/java/org/apache/commons/cli/ValueTest.java                       |   271 ++
  installer/test/java/org/apache/commons/cli/ValuesTest.java                      |   248 ++
  installer/test/java/org/python/util/install/ChildProcessExample.java            |    32 +
  installer/test/java/org/python/util/install/ChildProcessTest.java               |    80 +
  installer/test/java/org/python/util/install/ChmodTest_Standalone.java           |    57 +
  installer/test/java/org/python/util/install/FileHelperTest.java                 |   271 ++
  installer/test/java/org/python/util/install/FrameInstallerTest.java             |    88 +
  installer/test/java/org/python/util/install/InstallationTest.java               |   107 +
  installer/test/java/org/python/util/install/InstallationTypeTest.java           |   119 +
  installer/test/java/org/python/util/install/InstallerCommandLineTest.java       |   637 +++++
  installer/test/java/org/python/util/install/JavaHomeHandlerTest.java            |   114 +
  installer/test/java/org/python/util/install/JavaTest_Standalone.java            |    32 +
  installer/test/java/org/python/util/install/StandalonePackagerTest.java         |   183 +
  installer/test/java/org/python/util/install/StartScriptGeneratorTest.java       |   277 ++
  installer/test/java/org/python/util/install/UnicodeSequencesTest.java           |    34 +
  installer/test/java/org/python/util/install/driver/AutotestTest.java            |    78 +
  installer/test/java/org/python/util/install/driver/DrivableConsole.java         |    82 +
  installer/test/java/org/python/util/install/driver/DrivableConsoleTest.java     |    37 +
  installer/test/java/org/python/util/install/driver/NormalVerifierTest.java      |   131 +
  installer/test/java/org/python/util/install/driver/StandaloneVerifierTest.java  |    72 +
  lib-python/2.7/BaseHTTPServer.py                                                |     6 +-
  lib-python/2.7/CGIHTTPServer.py                                                 |    61 +-
  lib-python/2.7/Cookie.py                                                        |     4 +-
  lib-python/2.7/HTMLParser.py                                                    |     6 +-
  lib-python/2.7/SocketServer.py                                                  |    22 +-
  lib-python/2.7/StringIO.py                                                      |     2 +-
  lib-python/2.7/_LWPCookieJar.py                                                 |     4 +-
  lib-python/2.7/__future__.py                                                    |     2 +-
  lib-python/2.7/_osx_support.py                                                  |   488 ++++
  lib-python/2.7/_pyio.py                                                         |    20 +-
  lib-python/2.7/_strptime.py                                                     |    15 +-
  lib-python/2.7/aifc.py                                                          |    38 +-
  lib-python/2.7/argparse.py                                                      |    44 +-
  lib-python/2.7/asyncore.py                                                      |    20 +-
  lib-python/2.7/bdb.py                                                           |    15 +-
  lib-python/2.7/calendar.py                                                      |     7 +-
  lib-python/2.7/cgi.py                                                           |     1 -
  lib-python/2.7/cgitb.py                                                         |    11 +-
  lib-python/2.7/cmd.py                                                           |     1 +
  lib-python/2.7/collections.py                                                   |   187 +-
  lib-python/2.7/compiler/consts.py                                               |     2 +-
  lib-python/2.7/compiler/pycodegen.py                                            |     4 +-
  lib-python/2.7/compiler/symbols.py                                              |     4 +-
  lib-python/2.7/ctypes/test/test_bitfields.py                                    |    20 +
  lib-python/2.7/ctypes/test/test_numbers.py                                      |    10 +
  lib-python/2.7/ctypes/test/test_returnfuncptrs.py                               |    30 +
  lib-python/2.7/ctypes/test/test_structures.py                                   |     9 +
  lib-python/2.7/ctypes/test/test_win32.py                                        |     5 +-
  lib-python/2.7/ctypes/util.py                                                   |    29 +
  lib-python/2.7/curses/__init__.py                                               |     2 +-
  lib-python/2.7/decimal.py                                                       |     8 +-
  lib-python/2.7/distutils/__init__.py                                            |     2 +-
  lib-python/2.7/distutils/ccompiler.py                                           |     2 +
  lib-python/2.7/distutils/command/check.py                                       |     3 +
  lib-python/2.7/distutils/config.py                                              |     7 +-
  lib-python/2.7/distutils/dir_util.py                                            |     4 +
  lib-python/2.7/distutils/sysconfig.py                                           |   118 +-
  lib-python/2.7/distutils/tests/test_build_ext.py                                |     5 +-
  lib-python/2.7/distutils/tests/test_dir_util.py                                 |    18 +
  lib-python/2.7/distutils/tests/test_msvc9compiler.py                            |     2 +-
  lib-python/2.7/distutils/tests/test_register.py                                 |    33 +-
  lib-python/2.7/distutils/tests/test_sdist.py                                    |     7 +-
  lib-python/2.7/distutils/tests/test_sysconfig.py                                |    29 +
  lib-python/2.7/distutils/unixccompiler.py                                       |    70 +-
  lib-python/2.7/distutils/util.py                                                |    96 +-
  lib-python/2.7/doctest.py                                                       |    17 +-
  lib-python/2.7/email/_parseaddr.py                                              |     8 +-
  lib-python/2.7/email/base64mime.py                                              |     2 +-
  lib-python/2.7/email/feedparser.py                                              |     4 +-
  lib-python/2.7/email/generator.py                                               |    12 +-
  lib-python/2.7/email/test/test_email.py                                         |    29 +
  lib-python/2.7/email/test/test_email_renamed.py                                 |    32 +
  lib-python/2.7/email/utils.py                                                   |     4 +-
  lib-python/2.7/ftplib.py                                                        |    11 +-
  lib-python/2.7/glob.py                                                          |    25 +-
  lib-python/2.7/gzip.py                                                          |    81 +-
  lib-python/2.7/hashlib.py                                                       |     2 +-
  lib-python/2.7/heapq.py                                                         |    84 +-
  lib-python/2.7/httplib.py                                                       |    25 +-
  lib-python/2.7/idlelib/CallTips.py                                              |    33 +-
  lib-python/2.7/idlelib/ColorDelegator.py                                        |     9 +-
  lib-python/2.7/idlelib/EditorWindow.py                                          |    56 +-
  lib-python/2.7/idlelib/FormatParagraph.py                                       |     3 +-
  lib-python/2.7/idlelib/HyperParser.py                                           |     5 +
  lib-python/2.7/idlelib/IOBinding.py                                             |    37 +-
  lib-python/2.7/idlelib/NEWS.txt                                                 |    35 +-
  lib-python/2.7/idlelib/OutputWindow.py                                          |     6 +-
  lib-python/2.7/idlelib/PyShell.py                                               |   130 +-
  lib-python/2.7/idlelib/ReplaceDialog.py                                         |    30 +-
  lib-python/2.7/idlelib/config-extensions.def                                    |     2 +
  lib-python/2.7/idlelib/configDialog.py                                          |    18 +-
  lib-python/2.7/idlelib/configHandler.py                                         |    53 +-
  lib-python/2.7/idlelib/help.txt                                                 |    24 +-
  lib-python/2.7/idlelib/idlever.py                                               |     2 +-
  lib-python/2.7/idlelib/macosxSupport.py                                         |    16 +-
  lib-python/2.7/idlelib/run.py                                                   |    22 +-
  lib-python/2.7/io.py                                                            |    11 +-
  lib-python/2.7/json/__init__.py                                                 |    50 +-
  lib-python/2.7/json/decoder.py                                                  |    17 +-
  lib-python/2.7/json/encoder.py                                                  |    17 +-
  lib-python/2.7/json/tests/test_decode.py                                        |     9 +
  lib-python/2.7/json/tests/test_dump.py                                          |     9 +
  lib-python/2.7/json/tests/test_fail.py                                          |    24 +-
  lib-python/2.7/json/tests/test_float.py                                         |    15 +
  lib-python/2.7/json/tests/test_pass1.py                                         |    20 +-
  lib-python/2.7/json/tests/test_tool.py                                          |    69 +
  lib-python/2.7/json/tool.py                                                     |    17 +-
  lib-python/2.7/lib-tk/Tkinter.py                                                |    74 +-
  lib-python/2.7/lib-tk/test/test_ttk/test_functions.py                           |    40 +-
  lib-python/2.7/lib-tk/test/test_ttk/test_widgets.py                             |     8 +
  lib-python/2.7/lib-tk/tkSimpleDialog.py                                         |     2 +-
  lib-python/2.7/lib-tk/ttk.py                                                    |   126 +-
  lib-python/2.7/lib2to3/fixer_util.py                                            |    16 +-
  lib-python/2.7/lib2to3/pgen2/driver.py                                          |    17 +
  lib-python/2.7/lib2to3/refactor.py                                              |     2 +-
  lib-python/2.7/lib2to3/tests/test_fixers.py                                     |    12 +
  lib-python/2.7/locale.py                                                        |    10 +-
  lib-python/2.7/logging/__init__.py                                              |    17 +-
  lib-python/2.7/logging/handlers.py                                              |    72 +-
  lib-python/2.7/mailbox.py                                                       |    76 +-
  lib-python/2.7/mimetypes.py                                                     |     3 +-
  lib-python/2.7/multiprocessing/connection.py                                    |    11 +-
  lib-python/2.7/multiprocessing/dummy/__init__.py                                |     3 +-
  lib-python/2.7/multiprocessing/forking.py                                       |    20 +-
  lib-python/2.7/multiprocessing/pool.py                                          |    35 +-
  lib-python/2.7/multiprocessing/process.py                                       |     6 +-
  lib-python/2.7/multiprocessing/util.py                                          |    39 +-
  lib-python/2.7/plat-generic/regen                                               |     2 +-
  lib-python/2.7/platform.py                                                      |    37 +-
  lib-python/2.7/posixpath.py                                                     |    96 +-
  lib-python/2.7/pstats.py                                                        |    10 +-
  lib-python/2.7/py_compile.py                                                    |     2 +-
  lib-python/2.7/pyclbr.py                                                        |     2 +
  lib-python/2.7/pydoc.py                                                         |     7 +-
  lib-python/2.7/random.py                                                        |    20 +-
  lib-python/2.7/rfc822.py                                                        |     2 +-
  lib-python/2.7/rlcompleter.py                                                   |    36 +-
  lib-python/2.7/runpy.py                                                         |     2 +-
  lib-python/2.7/shutil.py                                                        |     8 +-
  lib-python/2.7/smtplib.py                                                       |     4 +-
  lib-python/2.7/socket.py                                                        |     4 +-
  lib-python/2.7/sqlite3/dbapi2.py                                                |     4 +-
  lib-python/2.7/sqlite3/dump.py                                                  |     9 +-
  lib-python/2.7/sqlite3/test/dump.py                                             |    23 +
  lib-python/2.7/sqlite3/test/hooks.py                                            |    19 +
  lib-python/2.7/sqlite3/test/regression.py                                       |    28 +-
  lib-python/2.7/sqlite3/test/userfunctions.py                                    |    60 +-
  lib-python/2.7/sre_compile.py                                                   |     1 +
  lib-python/2.7/sre_constants.py                                                 |     4 +-
  lib-python/2.7/sre_parse.py                                                     |    19 +-
  lib-python/2.7/ssl.py                                                           |    20 +-
  lib-python/2.7/string.py                                                        |     8 +-
  lib-python/2.7/subprocess.py                                                    |    55 +-
  lib-python/2.7/sysconfig.py                                                     |   158 +-
  lib-python/2.7/tarfile.py                                                       |    16 +-
  lib-python/2.7/telnetlib.py                                                     |   130 +
  lib-python/2.7/tempfile.py                                                      |    43 +-
  lib-python/2.7/test/crashers/buffer_mutate.py                                   |    30 +
  lib-python/2.7/test/keycert.pem                                                 |    59 +-
  lib-python/2.7/test/mp_fork_bomb.py                                             |    16 +
  lib-python/2.7/test/pickletester.py                                             |    35 +-
  lib-python/2.7/test/regrtest.py                                                 |     8 +-
  lib-python/2.7/test/sample_doctest_no_docstrings.py                             |    12 +
  lib-python/2.7/test/sample_doctest_no_doctests.py                               |    15 +
  lib-python/2.7/test/script_helper.py                                            |     8 +-
  lib-python/2.7/test/sha256.pem                                                  |   233 +-
  lib-python/2.7/test/string_tests.py                                             |    18 +
  lib-python/2.7/test/subprocessdata/sigchild_ignore.py                           |    11 +-
  lib-python/2.7/test/test_StringIO.py                                            |    42 +
  lib-python/2.7/test/test__osx_support.py                                        |   279 ++
  lib-python/2.7/test/test_aifc.py                                                |     7 +
  lib-python/2.7/test/test_argparse.py                                            |   137 +-
  lib-python/2.7/test/test_array.py                                               |    13 +
  lib-python/2.7/test/test_ast.py                                                 |     6 +
  lib-python/2.7/test/test_asyncore.py                                            |    27 +-
  lib-python/2.7/test/test_audioop.py                                             |   405 ++-
  lib-python/2.7/test/test_bigmem.py                                              |     8 +-
  lib-python/2.7/test/test_bisect.py                                              |    53 +-
  lib-python/2.7/test/test_builtin.py                                             |     6 +-
  lib-python/2.7/test/test_bytes.py                                               |    21 +
  lib-python/2.7/test/test_bz2.py                                                 |    81 +-
  lib-python/2.7/test/test_calendar.py                                            |    26 +-
  lib-python/2.7/test/test_capi.py                                                |    56 +-
  lib-python/2.7/test/test_cmd.py                                                 |     8 +-
  lib-python/2.7/test/test_cmd_line.py                                            |    38 +-
  lib-python/2.7/test/test_cmd_line_script.py                                     |    18 +-
  lib-python/2.7/test/test_codeccallbacks.py                                      |     6 +-
  lib-python/2.7/test/test_codecs.py                                              |   410 +++-
  lib-python/2.7/test/test_codeop.py                                              |     2 +-
  lib-python/2.7/test/test_compile.py                                             |    28 +
  lib-python/2.7/test/test_cookie.py                                              |    15 +-
  lib-python/2.7/test/test_cpickle.py                                             |    17 +-
  lib-python/2.7/test/test_csv.py                                                 |     9 +
  lib-python/2.7/test/test_decimal.py                                             |    12 +
  lib-python/2.7/test/test_deque.py                                               |    16 +
  lib-python/2.7/test/test_descr.py                                               |    26 +-
  lib-python/2.7/test/test_dict.py                                                |     8 +
  lib-python/2.7/test/test_dictcomps.py                                           |   117 +-
  lib-python/2.7/test/test_doctest.py                                             |    29 +-
  lib-python/2.7/test/test_docxmlrpc.py                                           |     2 +-
  lib-python/2.7/test/test_email.py                                               |     2 +
  lib-python/2.7/test/test_exceptions.py                                          |    12 +
  lib-python/2.7/test/test_fcntl.py                                               |    21 +
  lib-python/2.7/test/test_file2k.py                                              |   147 +-
  lib-python/2.7/test/test_file_eintr.py                                          |   239 ++
  lib-python/2.7/test/test_fileio.py                                              |    49 +-
  lib-python/2.7/test/test_format.py                                              |    10 +
  lib-python/2.7/test/test_functools.py                                           |    21 +-
  lib-python/2.7/test/test_gc.py                                                  |    69 +
  lib-python/2.7/test/test_gdb.py                                                 |    75 +-
  lib-python/2.7/test/test_generators.py                                          |     3 +-
  lib-python/2.7/test/test_genexps.py                                             |     3 +-
  lib-python/2.7/test/test_glob.py                                                |   115 +-
  lib-python/2.7/test/test_gzip.py                                                |    25 +
  lib-python/2.7/test/test_hashlib.py                                             |    50 +-
  lib-python/2.7/test/test_heapq.py                                               |    26 +
  lib-python/2.7/test/test_htmlparser.py                                          |    10 +
  lib-python/2.7/test/test_httplib.py                                             |    69 +-
  lib-python/2.7/test/test_httpservers.py                                         |    76 +-
  lib-python/2.7/test/test_imaplib.py                                             |     2 +-
  lib-python/2.7/test/test_import.py                                              |   103 +-
  lib-python/2.7/test/test_int.py                                                 |    63 +-
  lib-python/2.7/test/test_io.py                                                  |   165 +-
  lib-python/2.7/test/test_iter.py                                                |    15 +
  lib-python/2.7/test/test_itertools.py                                           |     6 +
  lib-python/2.7/test/test_logging.py                                             |    49 +-
  lib-python/2.7/test/test_long.py                                                |     6 +-
  lib-python/2.7/test/test_mailbox.py                                             |   208 +-
  lib-python/2.7/test/test_marshal.py                                             |    51 +-
  lib-python/2.7/test/test_memoryio.py                                            |    16 +-
  lib-python/2.7/test/test_minidom.py                                             |     2 +-
  lib-python/2.7/test/test_mmap.py                                                |    20 +
  lib-python/2.7/test/test_multiprocessing.py                                     |   173 +-
  lib-python/2.7/test/test_mutex.py                                               |     2 +-
  lib-python/2.7/test/test_old_mailbox.py                                         |    16 +-
  lib-python/2.7/test/test_optparse.py                                            |     7 +
  lib-python/2.7/test/test_os.py                                                  |    16 +-
  lib-python/2.7/test/test_parser.py                                              |    61 +-
  lib-python/2.7/test/test_pdb.py                                                 |    61 +-
  lib-python/2.7/test/test_peepholer.py                                           |    13 +-
  lib-python/2.7/test/test_pickle.py                                              |    20 +-
  lib-python/2.7/test/test_poll.py                                                |    10 +
  lib-python/2.7/test/test_posix.py                                               |   133 +-
  lib-python/2.7/test/test_posixpath.py                                           |   103 +-
  lib-python/2.7/test/test_property.py                                            |     4 +-
  lib-python/2.7/test/test_pty.py                                                 |     2 +-
  lib-python/2.7/test/test_pwd.py                                                 |     9 +
  lib-python/2.7/test/test_pyclbr.py                                              |     5 +
  lib-python/2.7/test/test_pydoc.py                                               |    43 +-
  lib-python/2.7/test/test_pyexpat.py                                             |    55 +-
  lib-python/2.7/test/test_random.py                                              |    56 +-
  lib-python/2.7/test/test_re.py                                                  |    94 +
  lib-python/2.7/test/test_readline.py                                            |     4 +
  lib-python/2.7/test/test_resource.py                                            |    17 +
  lib-python/2.7/test/test_sax.py                                                 |   166 +-
  lib-python/2.7/test/test_select.py                                              |     9 +
  lib-python/2.7/test/test_shutil.py                                              |    30 +
  lib-python/2.7/test/test_signal.py                                              |    16 +-
  lib-python/2.7/test/test_socket.py                                              |   111 +-
  lib-python/2.7/test/test_socketserver.py                                        |    41 +-
  lib-python/2.7/test/test_ssl.py                                                 |    50 +-
  lib-python/2.7/test/test_str.py                                                 |    27 +
  lib-python/2.7/test/test_strptime.py                                            |     8 +
  lib-python/2.7/test/test_struct.py                                              |    26 +-
  lib-python/2.7/test/test_subprocess.py                                          |    82 +
  lib-python/2.7/test/test_support.py                                             |   152 +-
  lib-python/2.7/test/test_sys.py                                                 |   207 +-
  lib-python/2.7/test/test_sys_settrace.py                                        |    13 +-
  lib-python/2.7/test/test_sysconfig.py                                           |     9 +
  lib-python/2.7/test/test_tarfile.py                                             |    32 +-
  lib-python/2.7/test/test_tcl.py                                                 |    22 +
  lib-python/2.7/test/test_telnetlib.py                                           |    92 +-
  lib-python/2.7/test/test_tempfile.py                                            |    87 +-
  lib-python/2.7/test/test_textwrap.py                                            |    62 +-
  lib-python/2.7/test/test_thread.py                                              |    23 +
  lib-python/2.7/test/test_threading.py                                           |    29 +
  lib-python/2.7/test/test_time.py                                                |     2 +-
  lib-python/2.7/test/test_tokenize.py                                            |    29 +
  lib-python/2.7/test/test_tools.py                                               |   339 +++-
  lib-python/2.7/test/test_ucn.py                                                 |    23 +
  lib-python/2.7/test/test_unicode.py                                             |    27 +
  lib-python/2.7/test/test_urllib.py                                              |    21 +
  lib-python/2.7/test/test_urllib2.py                                             |    58 +-
  lib-python/2.7/test/test_urllib2_localnet.py                                    |     8 +
  lib-python/2.7/test/test_urllib2net.py                                          |     4 +-
  lib-python/2.7/test/test_urlparse.py                                            |    49 +
  lib-python/2.7/test/test_uu.py                                                  |     4 +-
  lib-python/2.7/test/test_weakref.py                                             |   101 +-
  lib-python/2.7/test/test_winreg.py                                              |    45 +-
  lib-python/2.7/test/test_winsound.py                                            |     1 +
  lib-python/2.7/test/test_wsgiref.py                                             |   108 +-
  lib-python/2.7/test/test_xml_etree.py                                           |    20 +
  lib-python/2.7/test/test_xrange.py                                              |    75 +
  lib-python/2.7/test/test_zipfile.py                                             |   141 +-
  lib-python/2.7/test/test_zipimport_support.py                                   |    26 +-
  lib-python/2.7/test/test_zlib.py                                                |    37 +
  lib-python/2.7/test/testbz2_bigmem.bz2                                          |   Bin 
  lib-python/2.7/test/testtar.tar                                                 |   Bin 
  lib-python/2.7/textwrap.py                                                      |    12 +-
  lib-python/2.7/threading.py                                                     |     7 +-
  lib-python/2.7/tokenize.py                                                      |    14 +-
  lib-python/2.7/traceback.py                                                     |     2 +-
  lib-python/2.7/unittest/case.py                                                 |    17 +-
  lib-python/2.7/unittest/main.py                                                 |     5 +-
  lib-python/2.7/unittest/runner.py                                               |     2 +-
  lib-python/2.7/unittest/signals.py                                              |    16 +-
  lib-python/2.7/unittest/test/test_break.py                                      |    32 +
  lib-python/2.7/unittest/test/test_discovery.py                                  |    14 +
  lib-python/2.7/unittest/test/test_runner.py                                     |    13 +
  lib-python/2.7/unittest/test/test_skipping.py                                   |    30 +
  lib-python/2.7/urllib2.py                                                       |    11 +-
  lib-python/2.7/urlparse.py                                                      |    33 +-
  lib-python/2.7/wave.py                                                          |    12 +-
  lib-python/2.7/wsgiref/handlers.py                                              |    12 +-
  lib-python/2.7/wsgiref/validate.py                                              |     4 +-
  lib-python/2.7/xml/dom/minidom.py                                               |     5 +-
  lib-python/2.7/xml/etree/ElementTree.py                                         |     7 +-
  lib-python/2.7/xml/sax/_exceptions.py                                           |     6 +-
  lib-python/2.7/xml/sax/expatreader.py                                           |     5 +-
  lib-python/2.7/xml/sax/saxutils.py                                              |   114 +-
  lib-python/2.7/xml/sax/xmlreader.py                                             |     2 +-
  lib-python/2.7/xmlrpclib.py                                                     |     2 +-
  lib-python/2.7/zipfile.py                                                       |   551 ++--
  maven/build.xml                                                                 |     6 +-
  src/org/python/core/BaseBytes.java                                              |   402 ++-
  src/org/python/core/BuiltinDocs.java                                            |     4 +-
  src/org/python/core/PyBUF.java                                                  |    52 +-
  src/org/python/core/PyBuffer.java                                               |    43 +-
  src/org/python/core/PyByteArray.java                                            |   430 ++-
  src/org/python/core/PyDictionary.java                                           |    23 +-
  src/org/python/core/PyString.java                                               |    21 +-
  src/org/python/core/PySystemState.java                                          |    14 +
  src/org/python/core/PyXRange.java                                               |     3 +-
  src/org/python/core/buffer/BaseBuffer.java                                      |     9 +-
  src/org/python/core/buffer/SimpleBuffer.java                                    |     2 +
  src/org/python/core/buffer/SimpleStringBuffer.java                              |    10 +-
  src/org/python/core/buffer/Strided1DBuffer.java                                 |    25 +-
  src/org/python/core/buffer/Strided1DWritableBuffer.java                         |     2 +-
  src/org/python/core/codecs.java                                                 |    10 +-
  src/org/python/core/io/StreamIO.java                                            |    58 +-
  src/org/python/modules/SHA224Digest.java                                        |    12 +-
  src/org/python/modules/_codecs.java                                             |     8 +-
  src/org/python/modules/_csv/PyReader.java                                       |    12 +-
  src/org/python/modules/_csv/PyWriter.java                                       |    12 +-
  src/org/python/modules/_io/OpenMode.java                                        |     9 +-
  src/org/python/modules/_io/PyFileIO.java                                        |    69 +-
  src/org/python/modules/_io/PyIOBase.java                                        |     8 +-
  src/org/python/modules/_io/PyRawIOBase.java                                     |     2 +-
  src/org/python/modules/_sre.java                                                |     3 +
  src/org/python/modules/_threading/Condition.java                                |    14 +-
  src/org/python/modules/time/Time.java                                           |     5 +
  src/org/python/util/ProxyCompiler.java                                          |    15 +-
  tests/java/org/python/modules/_io/_ioTest.java                                  |   120 +-
  484 files changed, 30183 insertions(+), 5061 deletions(-)


diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -63,3 +63,5 @@
 4f5e3c12edc040058d724d4469a53e8649e64420 v2.7.0a1
 ac5609677c1382f14d0e04178fe6dd4108e4c231 v2.7.0a2
 3d2dbae23c5292b7f31ac4c92fa6d1afd8bd8eb2 v2.5.3
+f5b12dc4ff970c9594c99fc895772958c4a669a0 v2.5.4rc1
+8a556c4cc2810912e4ef277d53668ffc9d9f5772 v2.7b1
diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS
--- a/ACKNOWLEDGMENTS
+++ b/ACKNOWLEDGMENTS
@@ -93,11 +93,15 @@
     Costantino Cerbo
     Alex Groenholm
     Anselm Kruis
+    Andreas Stührk
     Dmitry Jemerov
     Miki Tebeka
     Jeff Allen
     Julian Kennedy
     Arfrever Frehtes Taifersar Arahesis
+    Andreas Stührk
+    Christian Klein
+    Jezreel Ng
 
 Local Variables:
 mode: indented-text
diff --git a/Lib/_jyio.py b/Lib/_jyio.py
--- a/Lib/_jyio.py
+++ b/Lib/_jyio.py
@@ -275,10 +275,48 @@
         self._buffer = buf
         self._pos = 0
 
+    # Jython: modelled after bytesio.c::bytesio_getstate
     def __getstate__(self):
-        if self.closed:
-            raise ValueError("__getstate__ on closed file")
-        return self.__dict__.copy()
+        d = getattr(self, '__dict__', None)
+        if d is not None :
+            d = d.copy()
+        return (self.getvalue(), self._pos, d)
+
+    # Jython: modelled after bytesio.c::bytesio_setstate
+    def __setstate__(self, state):
+
+        if not isinstance(state, tuple) or len(state) < 3 :
+            fmt = "%s.__setstate__ argument should be 3-tuple got %s"
+            raise TypeError( fmt % (type(self), type(state)) )
+
+        # Reset the object to its default state. This is only needed to handle
+        # the case of repeated calls to __setstate__. */
+        self._buffer = bytearray()
+        self._pos = 0
+
+        # Set the value of the internal buffer. If state[0] does not support the
+        # buffer protocol, bytesio_write will raise the appropriate TypeError. */
+        self.write(state[0]);
+
+        # Carefully set the position value. Alternatively, we could use the seek
+        # method instead of modifying self._pos directly to better protect the
+        # object internal state against erroneous (or malicious) inputs. */
+        p = state[1]
+        if not isinstance(p, (int, long)) :
+            fmt = "second item of state must be an integer, got %s"
+            raise TypeError( fmt % type(p) )
+        elif p < 0 :
+            raise ValueError("position value cannot be negative")
+        self._pos = p
+
+        # Set the dictionary of the instance variables. */
+        d = state[2]
+        if not d is None :
+            if isinstance(d, dict) :
+                self.__dict__ = d
+            else :
+                fmt = "third item of state should be a dict, got %s"
+                raise TypeError( fmt % type(d) )
 
     def getvalue(self):
         """Return the bytes value (contents) of the buffer
@@ -367,12 +405,15 @@
         return pos
 
     def readable(self):
+        self._checkClosed()
         return True
 
     def writable(self):
+        self._checkClosed()
         return True
 
     def seekable(self):
+        self._checkClosed()
         return True
 
 
@@ -1042,31 +1083,30 @@
 
     def seekable(self):
         self._checkInitialized()    # Jython: to forbid use in an invalid state
-        self._checkClosed()         # Jython: compatibility with C implementation
+        self._checkClosed()
         return self._seekable
 
     def readable(self):
         self._checkInitialized()    # Jython: to forbid use in an invalid state
-        self._checkClosed()         # Jython: compatibility with C implementation
         return self.buffer.readable()
 
     def writable(self):
         self._checkInitialized()    # Jython: to forbid use in an invalid state
-        self._checkClosed()         # Jython: compatibility with C implementation
         return self.buffer.writable()
 
     def flush(self):
-        self._checkInitialized()       # Jython: to forbid use in an invalid state
-        self._checkClosed()         # Jython: compatibility with C implementation
+        self._checkInitialized()    # Jython: to forbid use in an invalid state
         self.buffer.flush()
         self._telling = self._seekable
 
     def close(self):
         if self.buffer is not None and not self.closed:
-            # Jython difference: flush and close via super.
-            # Sets __closed for quick _checkClosed().
-            super(TextIOWrapper, self).close()
-            self.buffer.close()
+            try:
+                # Jython difference: flush and close via super.
+                # Sets __closed for quick _checkClosed().
+                super(TextIOWrapper, self).close()
+            finally:
+                self.buffer.close()
 
     # Jython difference: @property closed(self) inherited from _IOBase.__closed
 
@@ -1483,6 +1523,11 @@
     """
 
     def __init__(self, initial_value="", newline="\n"):
+
+        # Newline mark needs to be in bytes: convert if not already so
+        if isinstance(newline, unicode) :
+            newline = newline.encode("utf-8")
+
         super(StringIO, self).__init__(BytesIO(),
                                        encoding="utf-8",
                                        errors="strict",
@@ -1491,11 +1536,64 @@
         # C version, even under Windows.
         if newline is None:
             self._writetranslate = False
-        if initial_value:
-            if not isinstance(initial_value, unicode):
-                initial_value = unicode(initial_value)
-            self.write(initial_value)
-            self.seek(0)
+        # An initial value may have been supplied (and must be unicode)
+        if initial_value is not None:
+            if not isinstance(initial_value, unicode) :
+                fmt = "initial value should be unicode or None, got %s"
+                raise TypeError( fmt % type(initial_value) )
+            if initial_value:
+                self.write(initial_value)
+                self.seek(0)
+
+    # Jython: modelled after stringio.c::stringio_getstate
+    def __getstate__(self):
+        d = getattr(self, '__dict__', None)
+        if d is not None :
+            d = d.copy()
+        return (self.getvalue(), self._readnl, self.tell(), d)
+
+    # Jython: modelled after stringio.c:stringio_setstate
+    def __setstate__(self, state):
+        self._checkClosed()
+
+        if not isinstance(state, tuple) or len(state) < 4 :
+            fmt = "%s.__setstate__ argument should be 4-tuple got %s"
+            raise TypeError( fmt % (type(self), type(state)) )
+
+        # Initialize the object's state, but empty
+        self.__init__(None, state[1])
+
+        # Write the buffer, bypassing end-of-line translation.
+        value = state[0]
+        if value is not None:
+            if not isinstance(value, unicode) :
+                fmt = "ivalue should be unicode or None, got %s"
+                raise TypeError( fmt % type(value) )
+            encoder = self._encoder or self._get_encoder()
+            b = encoder.encode(state[0])
+            self.buffer.write(b)
+
+        # Reset the object to its default state. This is only needed to handle
+        # the case of repeated calls to __setstate__.
+        self.seek(0)
+
+        # Set the position value using seek. A long is tolerated (e.g from pickle).
+        p = state[2]
+        if not isinstance(p, (int, long)) :
+            fmt = "third item of state must be an integer, got %s"
+            raise TypeError( fmt % type(p) )
+        elif p < 0 :
+            raise ValueError("position value cannot be negative")
+        self.seek(p)
+
+        # Set the dictionary of the instance variables. */
+        d = state[3]
+        if not d is None :
+            if isinstance(d, dict) :
+                self.__dict__ = d
+            else :
+                fmt = "fourth item of state should be a dict, got %s"
+                raise TypeError( fmt % type(d) )
 
     def getvalue(self):
         self.flush()
diff --git a/Lib/_warnings.py b/Lib/_warnings.py
deleted file mode 100644
--- a/Lib/_warnings.py
+++ /dev/null
@@ -1,388 +0,0 @@
-"""
-XXX: faked out Java part of the warnings subsystem, mostly copied from
-     warnings.py. TODO: rewrite in Java!
-"""
-
-# Note: function level imports should *not* be used
-# in this module as it may cause import lock deadlock.
-# See bug 683658.
-import linecache
-import sys
-import types
-
-__all__ = ["filters", "default_action", "once_registry", "warn",
-           "warn_explicit"]
-
-onceregistry = once_registry = {}
-defaultaction = default_action = "default"
-
-def warnpy3k(message, category=None, stacklevel=1):
-    """Issue a deprecation warning for Python 3.x related changes.
-
-    Warnings are omitted unless Python is started with the -3 option.
-    """
-    if sys.py3kwarning:
-        if category is None:
-            category = DeprecationWarning
-        warn(message, category, stacklevel+1)
-
-def _show_warning(message, category, filename, lineno, file=None, line=None):
-    """Hook to write a warning to a file; replace if you like."""
-    if file is None:
-        file = sys.stderr
-    try:
-        file.write(formatwarning(message, category, filename, lineno, line))
-    except IOError:
-        pass # the file (probably stderr) is invalid - this warning gets lost.
-# Keep a working version around in case the deprecation of the old API is
-# triggered.
-showwarning = _show_warning
-
-def formatwarning(message, category, filename, lineno, line=None):
-    """Function to format a warning the standard way."""
-    s =  "%s:%s: %s: %s\n" % (filename, lineno, category.__name__, message)
-    line = linecache.getline(filename, lineno) if line is None else line
-    if line:
-        line = line.strip()
-        s += "  %s\n" % line
-    return s
-
-def filterwarnings(action, message="", category=Warning, module="", lineno=0,
-                   append=0):
-    """Insert an entry into the list of warnings filters (at the front).
-
-    'action' -- one of "error", "ignore", "always", "default", "module",
-                or "once"
-    'message' -- a regex that the warning message must match
-    'category' -- a class that the warning must be a subclass of
-    'module' -- a regex that the module name must match
-    'lineno' -- an integer line number, 0 matches all warnings
-    'append' -- if true, append to the list of filters
-    """
-    import re
-    assert action in ("error", "ignore", "always", "default", "module",
-                      "once"), "invalid action: %r" % (action,)
-    assert isinstance(message, basestring), "message must be a string"
-    assert isinstance(category, (type, types.ClassType)), \
-           "category must be a class"
-    assert issubclass(category, Warning), "category must be a Warning subclass"
-    assert isinstance(module, basestring), "module must be a string"
-    assert isinstance(lineno, int) and lineno >= 0, \
-           "lineno must be an int >= 0"
-    item = (action, re.compile(message, re.I), category,
-            re.compile(module), lineno)
-    if append:
-        filters.append(item)
-    else:
-        filters.insert(0, item)
-
-def simplefilter(action, category=Warning, lineno=0, append=0):
-    """Insert a simple entry into the list of warnings filters (at the front).
-
-    A simple filter matches all modules and messages.
-    'action' -- one of "error", "ignore", "always", "default", "module",
-                or "once"
-    'category' -- a class that the warning must be a subclass of
-    'lineno' -- an integer line number, 0 matches all warnings
-    'append' -- if true, append to the list of filters
-    """
-    assert action in ("error", "ignore", "always", "default", "module",
-                      "once"), "invalid action: %r" % (action,)
-    assert isinstance(lineno, int) and lineno >= 0, \
-           "lineno must be an int >= 0"
-    item = (action, None, category, None, lineno)
-    if append:
-        filters.append(item)
-    else:
-        filters.insert(0, item)
-
-def resetwarnings():
-    """Clear the list of warning filters, so that no filters are active."""
-    filters[:] = []
-
-class _OptionError(Exception):
-    """Exception used by option processing helpers."""
-    pass
-
-# Helper to process -W options passed via sys.warnoptions
-def _processoptions(args):
-    for arg in args:
-        try:
-            _setoption(arg)
-        except _OptionError, msg:
-            print >>sys.stderr, "Invalid -W option ignored:", msg
-
-# Helper for _processoptions()
-def _setoption(arg):
-    import re
-    parts = arg.split(':')
-    if len(parts) > 5:
-        raise _OptionError("too many fields (max 5): %r" % (arg,))
-    while len(parts) < 5:
-        parts.append('')
-    action, message, category, module, lineno = [s.strip()
-                                                 for s in parts]
-    action = _getaction(action)
-    message = re.escape(message)
-    category = _getcategory(category)
-    module = re.escape(module)
-    if module:
-        module = module + '$'
-    if lineno:
-        try:
-            lineno = int(lineno)
-            if lineno < 0:
-                raise ValueError
-        except (ValueError, OverflowError):
-            raise _OptionError("invalid lineno %r" % (lineno,))
-    else:
-        lineno = 0
-    filterwarnings(action, message, category, module, lineno)
-
-# Helper for _setoption()
-def _getaction(action):
-    if not action:
-        return "default"
-    if action == "all": return "always" # Alias
-    for a in ('default', 'always', 'ignore', 'module', 'once', 'error'):
-        if a.startswith(action):
-            return a
-    raise _OptionError("invalid action: %r" % (action,))
-
-# Helper for _setoption()
-def _getcategory(category):
-    import re
-    if not category:
-        return Warning
-    if re.match("^[a-zA-Z0-9_]+$", category):
-        try:
-            cat = eval(category)
-        except NameError:
-            raise _OptionError("unknown warning category: %r" % (category,))
-    else:
-        i = category.rfind(".")
-        module = category[:i]
-        klass = category[i+1:]
-        try:
-            m = __import__(module, None, None, [klass])
-        except ImportError:
-            raise _OptionError("invalid module name: %r" % (module,))
-        try:
-            cat = getattr(m, klass)
-        except AttributeError:
-            raise _OptionError("unknown warning category: %r" % (category,))
-    if not issubclass(cat, Warning):
-        raise _OptionError("invalid warning category: %r" % (category,))
-    return cat
-
-class SysGlobals:
-    '''sys.__dict__ values are reflectedfields, so we use this.'''
-    def __getitem__(self, key):
-        try:
-            return getattr(sys, key)
-        except AttributeError:
-            raise KeyError(key)
-
-    def get(self, key, default=None):
-        if key in self:
-            return self[key]
-        return default
-
-    def setdefault(self, key, default=None):
-        if key not in self:
-            sys.__dict__[key] = default
-        return self[key]
-
-    def __contains__(self, key):
-        return key in sys.__dict__
-
-# Code typically replaced by _warnings
-def warn(message, category=None, stacklevel=1):
-    """Issue a warning, or maybe ignore it or raise an exception."""
-    # Check if message is already a Warning object
-    if isinstance(message, Warning):
-        category = message.__class__
-    # Check category argument
-    if category is None:
-        category = UserWarning
-    assert issubclass(category, Warning)
-    # Get context information
-    try:
-        caller = sys._getframe(stacklevel)
-    except ValueError:
-        globals = SysGlobals()
-        lineno = 1
-    else:
-        globals = caller.f_globals
-        lineno = caller.f_lineno
-    if '__name__' in globals:
-        module = globals['__name__']
-    else:
-        module = "<string>"
-    filename = globals.get('__file__')
-    if filename:
-        fnl = filename.lower()
-        if fnl.endswith((".pyc", ".pyo")):
-            filename = filename[:-1]
-        elif fnl.endswith("$py.class"):
-            filename = filename[:-9] + '.py'
-    else:
-        if module == "__main__":
-            try:
-                filename = sys.argv[0]
-            except (AttributeError, TypeError):
-                # embedded interpreters don't have sys.argv, see bug #839151
-                filename = '__main__'
-        if not filename:
-            filename = module
-    registry = globals.setdefault("__warningregistry__", {})
-    warn_explicit(message, category, filename, lineno, module, registry,
-                  globals)
-
-def warn_explicit(message, category, filename, lineno,
-                  module=None, registry=None, module_globals=None):
-    lineno = int(lineno)
-    if module is None:
-        module = filename or "<unknown>"
-        if module[-3:].lower() == ".py":
-            module = module[:-3] # XXX What about leading pathname?
-    if registry is None:
-        registry = {}
-    if isinstance(message, Warning):
-        text = str(message)
-        category = message.__class__
-    else:
-        text = message
-        message = category(message)
-    key = (text, category, lineno)
-    # Quick test for common case
-    if registry.get(key):
-        return
-    # Search the filters
-    for item in filters:
-        action, msg, cat, mod, ln = item
-        if ((msg is None or msg.match(text)) and
-            issubclass(category, cat) and
-            (mod is None or mod.match(module)) and
-            (ln == 0 or lineno == ln)):
-            break
-    else:
-        action = defaultaction
-    # Early exit actions
-    if action == "ignore":
-        registry[key] = 1
-        return
-
-    # Prime the linecache for formatting, in case the
-    # "file" is actually in a zipfile or something.
-    linecache.getlines(filename, module_globals)
-
-    if action == "error":
-        raise message
-    # Other actions
-    if action == "once":
-        _onceregistry = globals().get('onceregistry', once_registry)
-        registry[key] = 1
-        oncekey = (text, category)
-        if _onceregistry.get(oncekey):
-            return
-        _onceregistry[oncekey] = 1
-    elif action == "always":
-        pass
-    elif action == "module":
-        registry[key] = 1
-        altkey = (text, category, 0)
-        if registry.get(altkey):
-            return
-        registry[altkey] = 1
-    elif action == "default":
-        registry[key] = 1
-    else:
-        # Unrecognized actions are errors
-        raise RuntimeError(
-              "Unrecognized action (%r) in warnings.filters:\n %s" %
-              (action, item))
-    # Print message and context
-    fn = globals().get('showwarning', _show_warning)
-    fn(message, category, filename. lineno)
-
-
-class WarningMessage(object):
-
-    """Holds the result of a single showwarning() call."""
-
-    _WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
-                        "line")
-
-    def __init__(self, message, category, filename, lineno, file=None,
-                    line=None):
-        local_values = locals()
-        for attr in self._WARNING_DETAILS:
-            setattr(self, attr, local_values[attr])
-        self._category_name = category.__name__ if category else None
-
-    def __str__(self):
-        return ("{message : %r, category : %r, filename : %r, lineno : %s, "
-                    "line : %r}" % (self.message, self._category_name,
-                                    self.filename, self.lineno, self.line))
-
-
-class catch_warnings(object):
-
-    """A context manager that copies and restores the warnings filter upon
-    exiting the context.
-
-    The 'record' argument specifies whether warnings should be captured by a
-    custom implementation of warnings.showwarning() and be appended to a list
-    returned by the context manager. Otherwise None is returned by the context
-    manager. The objects appended to the list are arguments whose attributes
-    mirror the arguments to showwarning().
-
-    The 'module' argument is to specify an alternative module to the module
-    named 'warnings' and imported under that name. This argument is only useful
-    when testing the warnings module itself.
-
-    """
-
-    def __init__(self, record=False, module=None):
-        """Specify whether to record warnings and if an alternative module
-        should be used other than sys.modules['warnings'].
-
-        For compatibility with Python 3.0, please consider all arguments to be
-        keyword-only.
-
-        """
-        self._record = record
-        self._module = sys.modules['warnings'] if module is None else module
-        self._entered = False
-
-    def __repr__(self):
-        args = []
-        if self._record:
-            args.append("record=True")
-        if self._module is not sys.modules['warnings']:
-            args.append("module=%r" % self._module)
-        name = type(self).__name__
-        return "%s(%s)" % (name, ", ".join(args))
-
-    def __enter__(self):
-        if self._entered:
-            raise RuntimeError("Cannot enter %r twice" % self)
-        self._entered = True
-        self._filters = self._module.filters
-        self._module.filters = self._module._filters = self._filters[:]
-        self._showwarning = self._module.showwarning
-        if self._record:
-            log = []
-            def showwarning(*args, **kwargs):
-                log.append(WarningMessage(*args, **kwargs))
-            self._module.showwarning = showwarning
-            return log
-        else:
-            return None
-
-    def __exit__(self, *exc_info):
-        if not self._entered:
-            raise RuntimeError("Cannot exit %r without entering first" % self)
-        self._module.filters = self._module._filters = self._filters
-        self._module.showwarning = self._showwarning
diff --git a/Lib/decimal.py b/Lib/decimal.py
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -1580,7 +1580,13 @@
 
     def __float__(self):
         """Float representation."""
-        return float(str(self))
+        if self._isnan():
+            if self.is_snan():
+                raise ValueError("Cannot convert signaling NaN to float")
+            s = "-nan" if self._sign else "nan"
+        else:
+            s = str(self)
+        return float(s)
 
     def __int__(self):
         """Converts self to an int, truncating if necessary."""
diff --git a/Lib/doctest.py b/Lib/doctest.py
--- a/Lib/doctest.py
+++ b/Lib/doctest.py
@@ -2318,7 +2318,8 @@
         return "Doctest: " + self._dt_test.name
 
 class SkipDocTestCase(DocTestCase):
-    def __init__(self):
+    def __init__(self, module):
+        self.module = module
         DocTestCase.__init__(self, None)
 
     def setUp(self):
@@ -2328,7 +2329,10 @@
         pass
 
     def shortDescription(self):
-        return "Skipping tests from %s" % module.__name__
+        return "Skipping tests from %s" % self.module.__name__
+
+    __str__ = shortDescription
+
 
 def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
                  **options):
@@ -2376,12 +2380,17 @@
     if not tests and sys.flags.optimize >=2:
         # Skip doctests when running with -O2
         suite = unittest.TestSuite()
-        suite.addTest(SkipDocTestCase())
+        suite.addTest(SkipDocTestCase(module))
         return suite
     elif not tests:
         # Why do we want to do this? Because it reveals a bug that might
         # otherwise be hidden.
-        raise ValueError(module, "has no tests")
+        # It is probably a bug that this exception is not also raised if the
+        # number of doctest examples in tests is zero (i.e. if no doctest
+        # examples were found).  However, we should probably not be raising
+        # an exception at all here, though it is too late to make this change
+        # for a maintenance release.  See also issue #14649.
+        raise ValueError(module, "has no docstrings")
 
     tests.sort()
     suite = unittest.TestSuite()
diff --git a/Lib/platform.py b/Lib/platform.py
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -34,6 +34,7 @@
 #
 #    <see CVS and SVN checkin messages for history>
 #
+#    1.0.7 - added DEV_NULL
 #    1.0.6 - added linux_distribution()
 #    1.0.5 - fixed Java support to allow running the module on Jython
 #    1.0.4 - added IronPython support
@@ -110,14 +111,28 @@
 
 """
 
-__version__ = '1.0.6'
+__version__ = '1.0.7'
 
 import sys,string,os,re
 
+### Globals & Constants
 if sys.platform.startswith("java"):
     from java.lang import System
     from org.python.core.Py import newString
 
+# Determine the platform's /dev/null device
+try:
+    DEV_NULL = os.devnull
+except AttributeError:
+    # os.devnull was added in Python 2.4, so emulate it for earlier
+    # Python versions
+    if sys.platform in ('dos','win32','win16','os2'):
+        # Use the old CP/M NUL as device name
+        DEV_NULL = 'NUL'
+    else:
+        # Standard Unix uses /dev/null
+        DEV_NULL = '/dev/null'
+
 ### Platform specific APIs
 
 _libc_search = re.compile(r'(__libc_init)'
@@ -171,7 +186,7 @@
         elif so:
             if lib != 'glibc':
                 lib = 'libc'
-                if soversion > version:
+                if soversion and soversion > version:
                     version = soversion
                 if threads and version[-len(threads):] != threads:
                     version = version + threads
@@ -276,24 +291,6 @@
             id = l[1]
     return '', version, id
 
-def _test_parse_release_file():
-
-    for input, output in (
-        # Examples of release file contents:
-        ('SuSE Linux 9.3 (x86-64)', ('SuSE Linux ', '9.3', 'x86-64'))
-        ('SUSE LINUX 10.1 (X86-64)', ('SUSE LINUX ', '10.1', 'X86-64'))
-        ('SUSE LINUX 10.1 (i586)', ('SUSE LINUX ', '10.1', 'i586'))
-        ('Fedora Core release 5 (Bordeaux)', ('Fedora Core', '5', 'Bordeaux'))
-        ('Red Hat Linux release 8.0 (Psyche)', ('Red Hat Linux', '8.0', 'Psyche'))
-        ('Red Hat Linux release 9 (Shrike)', ('Red Hat Linux', '9', 'Shrike'))
-        ('Red Hat Enterprise Linux release 4 (Nahant)', ('Red Hat Enterprise Linux', '4', 'Nahant'))
-        ('CentOS release 4', ('CentOS', '4', None))
-        ('Rocks release 4.2.1 (Cydonia)', ('Rocks', '4.2.1', 'Cydonia'))
-        ):
-        parsed = _parse_release_file(input)
-        if parsed != output:
-            print (input, parsed)
-
 def linux_distribution(distname='', version='', id='',
 
                        supported_dists=_supported_dists,
@@ -474,7 +471,16 @@
 
 _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '
                          '.*'
-                         'Version ([\d.]+))')
+                         '\[.* ([\d.]+)\])')
+
+# Examples of VER command output:
+#
+#   Windows 2000:  Microsoft Windows 2000 [Version 5.00.2195]
+#   Windows XP:    Microsoft Windows XP [Version 5.1.2600]
+#   Windows Vista: Microsoft Windows [Version 6.0.6002]
+#
+# Note that the "Version" string gets localized on different
+# Windows versions.
 
 def _syscmd_ver(system='', release='', version='',
 
@@ -500,7 +506,7 @@
             info = pipe.read()
             if pipe.close():
                 raise os.error,'command failed'
-            # XXX How can I supress shell errors from being written
+            # XXX How can I suppress shell errors from being written
             #     to stderr ?
         except os.error,why:
             #print 'Command %s failed: %s' % (cmd,why)
@@ -551,7 +557,7 @@
 
     """ Get additional version information from the Windows Registry
         and return a tuple (version,csd,ptype) referring to version
-        number, CSD level and OS type (multi/single
+        number, CSD level (service pack), and OS type (multi/single
         processor).
 
         As a hint: ptype returns 'Uniprocessor Free' on single
@@ -613,6 +619,7 @@
     else:
         if csd[:13] == 'Service Pack ':
             csd = 'SP' + csd[13:]
+
     if plat == VER_PLATFORM_WIN32_WINDOWS:
         regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
         # Try to guess the release name
@@ -627,6 +634,7 @@
                 release = 'postMe'
         elif maj == 5:
             release = '2000'
+
     elif plat == VER_PLATFORM_WIN32_NT:
         regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
         if maj <= 4:
@@ -668,8 +676,14 @@
                     release = '7'
                 else:
                     release = '2008ServerR2'
+            elif min == 2:
+                if product_type == VER_NT_WORKSTATION:
+                    release = '8'
+                else:
+                    release = '2012Server'
             else:
-                release = 'post2008Server'
+                release = 'post2012Server'
+
     else:
         if not release:
             # E.g. Win3.1 with win32s
@@ -759,6 +773,7 @@
                    0x2: 'PowerPC',
                    0xa: 'i386'}.get(sysa,'')
 
+    versioninfo=('', '', '')
     return release,versioninfo,machine
 
 def _mac_ver_xml():
@@ -988,11 +1003,12 @@
 
     """ Interface to the system's uname command.
     """
-    if sys.platform in ('dos','win32','win16','os2'):
+    if sys.platform in ('dos','win32','win16','os2') or \
+       (sys.platform.startswith('java') and os._name == 'nt'):
         # XXX Others too ?
         return default
     try:
-        f = os.popen('uname %s 2> /dev/null' % option)
+        f = os.popen('uname %s 2> %s' % (option, DEV_NULL))
     except (AttributeError,os.error):
         return default
     output = string.strip(f.read())
@@ -1012,16 +1028,38 @@
         case the command should fail.
 
     """
+
+    # We do the import here to avoid a bootstrap issue.
+    # See c73b90b6dadd changeset.
+    #
+    # [..]
+    # ranlib libpython2.7.a
+    # gcc   -o python \
+    #        Modules/python.o \
+    #        libpython2.7.a -lsocket -lnsl -ldl    -lm
+    # Traceback (most recent call last):
+    #  File "./setup.py", line 8, in <module>
+    #    from platform import machine as platform_machine
+    #  File "[..]/build/Lib/platform.py", line 116, in <module>
+    #    import sys,string,os,re,subprocess
+    #  File "[..]/build/Lib/subprocess.py", line 429, in <module>
+    #    import select
+    # ImportError: No module named select
+
+    import subprocess
+
     if sys.platform in ('dos','win32','win16','os2'):
         # XXX Others too ?
         return default
-    target = _follow_symlinks(target).replace('"', '\\"')
+    target = _follow_symlinks(target)
     try:
-        f = os.popen('file "%s" 2> /dev/null' % target)
+        proc = subprocess.Popen(['file', target],
+                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
     except (AttributeError,os.error):
         return default
-    output = string.strip(f.read())
-    rc = f.close()
+    output = proc.communicate()[0]
+    rc = proc.wait()
     if not output or rc:
         return default
     else:
@@ -1164,7 +1202,7 @@
             node = _node()
             machine = ''
 
-        use_syscmd_ver = 01
+        use_syscmd_ver = 1
 
         # Try win32_ver() on win32 platforms
         if system == 'win32':
@@ -1176,7 +1214,11 @@
             # http://support.microsoft.com/kb/888731 and
             # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM
             if not machine:
-                machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
+                # WOW64 processes mask the native architecture
+                if "PROCESSOR_ARCHITEW6432" in os.environ:
+                    machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')
+                else:
+                    machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')
             if not processor:
                 processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)
 
@@ -1216,10 +1258,6 @@
             if not version:
                 version = vendor
 
-        elif os.name == 'mac':
-            release,(version,stage,nonrel),machine = mac_ver()
-            system = 'MacOS'
-
     # System specific extensions
     if system == 'OpenVMS':
         # OpenVMS seems to have release and version mixed up
@@ -1339,6 +1377,11 @@
     '(?: \(([\d\.]+)\))?'
     ' on (.NET [\d\.]+)')
 
+_pypy_sys_version_parser = re.compile(
+    r'([\w.+]+)\s*'
+    '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
+    '\[PyPy [^\]]+\]?')
+
 _sys_version_cache = {}
 
 def _sys_version(sys_version=None):
@@ -1400,6 +1443,16 @@
         buildno = ''
         builddate = ''
 
+    elif "PyPy" in sys_version:
+        # PyPy
+        name = "PyPy"
+        match = _pypy_sys_version_parser.match(sys_version)
+        if match is None:
+            raise ValueError("failed to parse PyPy sys.version: %s" %
+                             repr(sys_version))
+        version, buildno, builddate, buildtime = match.groups()
+        compiler = ""
+
     else:
         # CPython
         match = _sys_version_parser.match(sys_version)
@@ -1409,15 +1462,16 @@
                 repr(sys_version))
         version, buildno, builddate, buildtime, compiler = \
               match.groups()
-        if hasattr(sys, 'subversion'):
-            # sys.subversion was added in Python 2.5
-            name, branch, revision = sys.subversion
-        else:
-            name = 'CPython'
-            branch = ''
-            revision = ''
+        name = 'CPython'
         builddate = builddate + ' ' + buildtime
 
+    if hasattr(sys, 'subversion'):
+        # sys.subversion was added in Python 2.5
+        _, branch, revision = sys.subversion
+    else:
+        branch = ''
+        revision = ''
+
     # Add the patchlevel version if missing
     l = string.split(version, '.')
     if len(l) == 2:
@@ -1429,29 +1483,15 @@
     _sys_version_cache[sys_version] = result
     return result
 
-def _test_sys_version():
-
-    _sys_version_cache.clear()
-    for input, output in (
-        ('2.4.3 (#1, Jun 21 2006, 13:54:21) \n[GCC 3.3.4 (pre 3.3.5 20040809)]',
-         ('CPython', '2.4.3', '', '', '1', 'Jun 21 2006 13:54:21', 'GCC 3.3.4 (pre 3.3.5 20040809)')),
-        ('IronPython 1.0.60816 on .NET 2.0.50727.42',
-         ('IronPython', '1.0.60816', '', '', '', '', '.NET 2.0.50727.42')),
-        ('IronPython 1.0 (1.0.61005.1977) on .NET 2.0.50727.42',
-         ('IronPython', '1.0.0', '', '', '', '', '.NET 2.0.50727.42')),
-        ):
-        parsed = _sys_version(input)
-        if parsed != output:
-            print (input, parsed)
-
 def python_implementation():
 
     """ Returns a string identifying the Python implementation.
 
         Currently, the following implementations are identified:
-        'CPython' (C implementation of Python),
-        'IronPython' (.NET implementation of Python),
-        'Jython' (Java implementation of Python).
+          'CPython' (C implementation of Python),
+          'IronPython' (.NET implementation of Python),
+          'Jython' (Java implementation of Python),
+          'PyPy' (Python implementation of Python).
 
     """
     return _sys_version()[0]
diff --git a/Lib/socket.py b/Lib/socket.py
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -162,26 +162,24 @@
 from functools import wraps
 
 # Used to map java exceptions to the equivalent python exception
+# And to set the _last_error attribute on socket objects, to support SO_ERROR
 def raises_java_exception(method_or_function):
     @wraps(method_or_function)
-    def map_exception(*args, **kwargs):
+    def handle_exception(*args, **kwargs):
+        is_socket = (len(args) > 0 and isinstance(args[0], _nonblocking_api_mixin))
         try:
-            return method_or_function(*args, **kwargs)
-        except java.lang.Exception, jlx:
-            raise _map_exception(jlx)
-    return map_exception
-
-# Used for SO_ERROR support.
-def raises_error(method):
-    @wraps(method)
-    def set_last_error(obj, *args, **kwargs):
-        try:
-            setattr(obj, '_last_error', 0)
-            return method(obj, *args, **kwargs)
+            try:
+                return method_or_function(*args, **kwargs)
+            except java.lang.Exception, jlx:
+                raise _map_exception(jlx)
         except error, e:
-            setattr(obj, '_last_error', e[0])
+            if is_socket:
+                setattr(args[0], '_last_error', e[0])
             raise
-    return set_last_error
+        else:
+            if is_socket:
+                setattr(args[0], '_last_error', 0)
+    return handle_exception
 
 _feature_support_map = {
     'ipv6': True,
@@ -337,7 +335,7 @@
     'htonl', 'ntohs', 'ntohl', 'inet_pton', 'inet_ntop', 'inet_aton',
     'inet_ntoa', 'create_connection', 'socket', 'ssl',
     # exceptions
-    'error', 'herror', 'gaierror', 'timeout', 'sslerror,
+    'error', 'herror', 'gaierror', 'timeout', 'sslerror',
     # classes
     'SocketType', 
     # Misc flags     
@@ -695,6 +693,7 @@
         addrs.append(asPyString(addr.getHostAddress()))
     return (names, addrs)
 
+ at raises_java_exception
 def getfqdn(name=None):
     """
     Return a fully qualified domain name for name. If name is omitted or empty
@@ -1145,7 +1144,6 @@
     def getblocking(self):
         return self.mode == MODE_BLOCKING
 
-    @raises_error
     @raises_java_exception
     def setsockopt(self, level, optname, value):
         if self.sock_impl:
@@ -1168,7 +1166,6 @@
         else:
             return self.pending_options.get( (level, optname), None)
 
-    @raises_error
     @raises_java_exception
     def shutdown(self, how):
         assert how in (SHUT_RD, SHUT_WR, SHUT_RDWR)
@@ -1176,13 +1173,11 @@
             raise error(errno.ENOTCONN, "Transport endpoint is not connected")
         self.sock_impl.shutdown(how)
 
-    @raises_error
     @raises_java_exception
     def close(self):
         if self.sock_impl:
             self.sock_impl.close()
 
-    @raises_error
     @raises_java_exception
     def getsockname(self):
         if self.sock_impl is None:
@@ -1194,7 +1189,6 @@
             raise error(errno.EINVAL, "Invalid argument")
         return self.sock_impl.getsockname()
 
-    @raises_error
     @raises_java_exception
     def getpeername(self):
         if self.sock_impl is None:
@@ -1239,7 +1233,6 @@
             return self.server
         return _nonblocking_api_mixin.getsockopt(self, level, optname)
 
-    @raises_error
     @raises_java_exception
     def bind(self, addr):
         assert not self.sock_impl
@@ -1248,7 +1241,6 @@
         _get_jsockaddr(addr, self.family, self.type, self.proto, AI_PASSIVE)
         self.local_addr = addr
 
-    @raises_error
     @raises_java_exception
     def listen(self, backlog):
         "This signifies a server socket"
@@ -1258,7 +1250,6 @@
                               backlog, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
         self._config()
 
-    @raises_error
     @raises_java_exception
     def accept(self):
         "This signifies a server socket"
@@ -1283,14 +1274,12 @@
         self._config() # Configure timeouts, etc, now that the socket exists
         self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0))
 
-    @raises_error
     @raises_java_exception
     def connect(self, addr):
         "This signifies a client socket"
         self._do_connect(addr)
         self._setup()
 
-    @raises_error
     @raises_java_exception
     def connect_ex(self, addr):
         "This signifies a client socket"
@@ -1308,7 +1297,6 @@
             self.istream = self.sock_impl.jsocket.getInputStream()
             self.ostream = self.sock_impl.jsocket.getOutputStream()
 
-    @raises_error
     @raises_java_exception
     def recv(self, n):
         if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected')
@@ -1326,12 +1314,10 @@
             data = data[:m]
         return data.tostring()
 
-    @raises_error
     @raises_java_exception
     def recvfrom(self, n):
-        return self.recv(n), None
+        return self.recv(n), self.getpeername()
 
-    @raises_error
     @raises_java_exception
     def send(self, s):
         if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected')
@@ -1344,7 +1330,6 @@
 
     sendall = send
 
-    @raises_error
     @raises_java_exception
     def close(self):
         if self.istream:
@@ -1365,7 +1350,6 @@
     def __init__(self):
         _nonblocking_api_mixin.__init__(self)
 
-    @raises_error
     @raises_java_exception
     def bind(self, addr):
         assert not self.sock_impl
@@ -1385,19 +1369,16 @@
         self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0))
         self.connected = True
 
-    @raises_error
     @raises_java_exception
     def connect(self, addr):
         self._do_connect(addr)
 
-    @raises_error
     @raises_java_exception
     def connect_ex(self, addr):
         if not self.sock_impl:
             self._do_connect(addr)
         return 0
 
-    @raises_error
     @raises_java_exception
     def sendto(self, data, p1, p2=None):
         if not p2:
@@ -1417,7 +1398,6 @@
         byte_array = java.lang.String(data).getBytes('iso-8859-1')
         return self.sock_impl.send(byte_array, flags)
 
-    @raises_error
     @raises_java_exception
     def recvfrom(self, num_bytes, flags=None):
         """
@@ -1437,7 +1417,6 @@
             self._config()
         return self.sock_impl.recvfrom(num_bytes, flags)
 
-    @raises_error
     @raises_java_exception
     def recv(self, num_bytes, flags=None):
         if not self.sock_impl:
@@ -1857,11 +1836,6 @@
         java_ssl_socket.startHandshake()
         return java_ssl_socket
 
-    def __getattr__(self, attr_name):
-        if hasattr(self.jython_socket_wrapper, attr_name):
-            return getattr(self.jython_socket_wrapper, attr_name)
-        raise AttributeError(attr_name)
-
     @raises_java_exception
     def read(self, n=4096):
         data = jarray.zeros(n, 'b')
@@ -1872,24 +1846,35 @@
             data = data[:m]
         return data.tostring()
 
+    recv = read
+
     @raises_java_exception
     def write(self, s):
         self._out_buf.write(s)
         self._out_buf.flush()
         return len(s)
 
-    @raises_java_exception
+    send = sendall = write
+
+    def makefile(self, mode='r', bufsize=-1):
+        return _fileobject(self, mode, bufsize)
+
     def _get_server_cert(self):
         return self.java_ssl_socket.getSession().getPeerCertificates()[0]
 
+    @raises_java_exception
     def server(self):
         cert = self._get_server_cert()
         return cert.getSubjectDN().toString()
 
+    @raises_java_exception
     def issuer(self):
         cert = self._get_server_cert()
         return cert.getIssuerDN().toString()
 
+    def close(self):
+        self.jython_socket_wrapper.close()
+
 def test():
     s = socket(AF_INET, SOCK_STREAM)
     s.connect(("", 80))
diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py
--- a/Lib/test/string_tests.py
+++ b/Lib/test/string_tests.py
@@ -1274,11 +1274,9 @@
         self.checkequal('Abc', 'abc', 'translate', table)
         self.checkequal('xyz', 'xyz', 'translate', table)
         self.checkequal('yz', 'xyz', 'translate', table, 'x')
-        #FIXME:
-        if not test_support.is_jython:
-            self.checkequal('yx', 'zyzzx', 'translate', None, 'z')
-            self.checkequal('zyzzx', 'zyzzx', 'translate', None, '')
-            self.checkequal('zyzzx', 'zyzzx', 'translate', None)
+        self.checkequal('yx', 'zyzzx', 'translate', None, 'z')
+        self.checkequal('zyzzx', 'zyzzx', 'translate', None, '')
+        self.checkequal('zyzzx', 'zyzzx', 'translate', None)
         self.checkraises(ValueError, 'xyz', 'translate', 'too short', 'strip')
         self.checkraises(ValueError, 'xyz', 'translate', 'too short')
 
diff --git a/Lib/test/test_charmapcodec.py b/Lib/test/test_charmapcodec.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_charmapcodec.py
@@ -0,0 +1,61 @@
+""" Python character mapping codec test
+
+This uses the test codec in testcodec.py and thus also tests the
+encodings package lookup scheme.
+
+Written by Marc-Andre Lemburg (mal at lemburg.com).
+
+(c) Copyright 2000 Guido van Rossum.
+
+"""#"
+
+import test.test_support, unittest
+
+import codecs
+
+# Register a search function which knows about our codec
+def codec_search_function(encoding):
+    if encoding == 'testcodec':
+        from test import testcodec
+        return tuple(testcodec.getregentry())
+    return None
+
+codecs.register(codec_search_function)
+
+# test codec's name (see test/testcodec.py)
+codecname = 'testcodec'
+
+class CharmapCodecTest(unittest.TestCase):
+    def test_constructorx(self):
+        self.assertEqual(unicode('abc', codecname), u'abc')
+        self.assertEqual(unicode('xdef', codecname), u'abcdef')
+        self.assertEqual(unicode('defx', codecname), u'defabc')
+        self.assertEqual(unicode('dxf', codecname), u'dabcf')
+        self.assertEqual(unicode('dxfx', codecname), u'dabcfabc')
+
+    def test_encodex(self):
+        self.assertEqual(u'abc'.encode(codecname), 'abc')
+        self.assertEqual(u'xdef'.encode(codecname), 'abcdef')
+        self.assertEqual(u'defx'.encode(codecname), 'defabc')
+        self.assertEqual(u'dxf'.encode(codecname), 'dabcf')
+        self.assertEqual(u'dxfx'.encode(codecname), 'dabcfabc')
+
+    # This test isn't working on Ubuntu on an Apple Intel powerbook,
+    # Jython 2.7b1+ (default:6b4a1088566e, Feb 10 2013, 14:36:47) 
+    # [OpenJDK 64-Bit Server VM (Oracle Corporation)] on java1.7.0_09
+    @unittest.skipIf(test.test_support.is_jython,
+                     "FIXME: Currently not working on jython")
+    def test_constructory(self):
+        self.assertEqual(unicode('ydef', codecname), u'def')
+        self.assertEqual(unicode('defy', codecname), u'def')
+        self.assertEqual(unicode('dyf', codecname), u'df')
+        self.assertEqual(unicode('dyfy', codecname), u'df')
+
+    def test_maptoundefined(self):
+        self.assertRaises(UnicodeError, unicode, 'abc\001', codecname)
+
+def test_main():
+    test.test_support.run_unittest(CharmapCodecTest)
+
+if __name__ == "__main__":
+    test_main()
diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -1084,7 +1084,7 @@
                 except Exception,e:
                     raise test_support.TestFailed("Test 3.%d: %s" % (pos+1, str(e)))
 
- at unittest.skipIf(test_support.is_jython, "FIXME: Jython issue 2000 missing support for IDNA")
+ at unittest.skipIf(test_support.is_jython, "FIXME: Jython issue 1153 missing support for IDNA")
 class IDNACodecTest(unittest.TestCase):
     def test_builtin_decode(self):
         self.assertEqual(unicode("python.org", "idna"), u"python.org")
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -518,9 +518,7 @@
         s &= WithSet('cdef')            # This used to fail
         self.assertEqual(set(s), set('cd'))
 
-    @unittest.skipIf(test_support.is_jython, "FIXME: doesn't work in Jython")
     def test_issue_4920(self):
-        # MutableSet.pop() method did not work
         class MySet(collections.MutableSet):
             __slots__=['__s']
             def __init__(self,items=None):
@@ -543,8 +541,9 @@
                 return result
             def __repr__(self):
                 return "MySet(%s)" % repr(list(self))
-        s = MySet([5,43,2,1])
-        self.assertEqual(s.pop(), 1)
+        values = [5,43,2,1]
+        s = MySet(values)
+        self.assertIn(s.pop(), values)
 
     def test_issue8750(self):
         empty = WithSet()
diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py
deleted file mode 100644
--- a/Lib/test/test_csv.py
+++ /dev/null
@@ -1,1072 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-# Copyright (C) 2001,2002 Python Software Foundation
-# csv package unit tests
-
-import sys
-import os
-import unittest
-from StringIO import StringIO
-import tempfile
-import csv
-import gc
-import io
-from test import test_support
-
-class Test_Csv(unittest.TestCase):
-    """
-    Test the underlying C csv parser in ways that are not appropriate
-    from the high level interface. Further tests of this nature are done
-    in TestDialectRegistry.
-    """
-    def _test_arg_valid(self, ctor, arg):
-        self.assertRaises(TypeError, ctor)
-        self.assertRaises(TypeError, ctor, None)
-        self.assertRaises(TypeError, ctor, arg, bad_attr = 0)
-        self.assertRaises(TypeError, ctor, arg, delimiter = 0)
-        self.assertRaises(TypeError, ctor, arg, delimiter = 'XX')
-        self.assertRaises(csv.Error, ctor, arg, 'foo')
-        self.assertRaises(TypeError, ctor, arg, delimiter=None)
-        self.assertRaises(TypeError, ctor, arg, delimiter=1)
-        self.assertRaises(TypeError, ctor, arg, quotechar=1)
-        self.assertRaises(TypeError, ctor, arg, lineterminator=None)
-        self.assertRaises(TypeError, ctor, arg, lineterminator=1)
-        self.assertRaises(TypeError, ctor, arg, quoting=None)
-        self.assertRaises(TypeError, ctor, arg,
-                          quoting=csv.QUOTE_ALL, quotechar='')
-        self.assertRaises(TypeError, ctor, arg,
-                          quoting=csv.QUOTE_ALL, quotechar=None)
-
-    def test_reader_arg_valid(self):
-        self._test_arg_valid(csv.reader, [])
-
-    def test_writer_arg_valid(self):
-        self._test_arg_valid(csv.writer, StringIO())
-
-    def _test_default_attrs(self, ctor, *args):
-        obj = ctor(*args)
-        # Check defaults
-        self.assertEqual(obj.dialect.delimiter, ',')
-        self.assertEqual(obj.dialect.doublequote, True)
-        self.assertEqual(obj.dialect.escapechar, None)
-        self.assertEqual(obj.dialect.lineterminator, "\r\n")
-        self.assertEqual(obj.dialect.quotechar, '"')
-        self.assertEqual(obj.dialect.quoting, csv.QUOTE_MINIMAL)
-        self.assertEqual(obj.dialect.skipinitialspace, False)
-        self.assertEqual(obj.dialect.strict, False)
-        # Try deleting or changing attributes (they are read-only)
-        self.assertRaises(TypeError, delattr, obj.dialect, 'delimiter')
-        self.assertRaises(TypeError, setattr, obj.dialect, 'delimiter', ':')
-        self.assertRaises(AttributeError, delattr, obj.dialect, 'quoting')
-        self.assertRaises(AttributeError, setattr, obj.dialect,
-                          'quoting', None)
-
-    def test_reader_attrs(self):
-        self._test_default_attrs(csv.reader, [])
-
-    def test_writer_attrs(self):
-        self._test_default_attrs(csv.writer, StringIO())
-
-    def _test_kw_attrs(self, ctor, *args):
-        # Now try with alternate options
-        kwargs = dict(delimiter=':', doublequote=False, escapechar='\\',
-                      lineterminator='\r', quotechar='*',
-                      quoting=csv.QUOTE_NONE, skipinitialspace=True,
-                      strict=True)
-        obj = ctor(*args, **kwargs)
-        self.assertEqual(obj.dialect.delimiter, ':')
-        self.assertEqual(obj.dialect.doublequote, False)
-        self.assertEqual(obj.dialect.escapechar, '\\')
-        self.assertEqual(obj.dialect.lineterminator, "\r")
-        self.assertEqual(obj.dialect.quotechar, '*')
-        self.assertEqual(obj.dialect.quoting, csv.QUOTE_NONE)
-        self.assertEqual(obj.dialect.skipinitialspace, True)
-        self.assertEqual(obj.dialect.strict, True)
-
-    def test_reader_kw_attrs(self):
-        self._test_kw_attrs(csv.reader, [])
-
-    def test_writer_kw_attrs(self):
-        self._test_kw_attrs(csv.writer, StringIO())
-
-    def _test_dialect_attrs(self, ctor, *args):
-        # Now try with dialect-derived options
-        class dialect:
-            delimiter='-'
-            doublequote=False
-            escapechar='^'
-            lineterminator='$'
-            quotechar='#'
-            quoting=csv.QUOTE_ALL
-            skipinitialspace=True
-            strict=False
-        args = args + (dialect,)
-        obj = ctor(*args)
-        self.assertEqual(obj.dialect.delimiter, '-')
-        self.assertEqual(obj.dialect.doublequote, False)
-        self.assertEqual(obj.dialect.escapechar, '^')
-        self.assertEqual(obj.dialect.lineterminator, "$")
-        self.assertEqual(obj.dialect.quotechar, '#')
-        self.assertEqual(obj.dialect.quoting, csv.QUOTE_ALL)
-        self.assertEqual(obj.dialect.skipinitialspace, True)
-        self.assertEqual(obj.dialect.strict, False)
-
-    def test_reader_dialect_attrs(self):
-        self._test_dialect_attrs(csv.reader, [])
-
-    def test_writer_dialect_attrs(self):
-        self._test_dialect_attrs(csv.writer, StringIO())
-
-
-    def _write_test(self, fields, expect, **kwargs):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj, **kwargs)
-            writer.writerow(fields)
-            fileobj.seek(0)
-            self.assertEqual(fileobj.read(),
-                             expect + writer.dialect.lineterminator)
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_write_arg_valid(self):
-        self.assertRaises(csv.Error, self._write_test, None, '')
-        self._write_test((), '')
-        self._write_test([None], '""')
-        self.assertRaises(csv.Error, self._write_test,
-                          [None], None, quoting = csv.QUOTE_NONE)
-        # Check that exceptions are passed up the chain
-        class BadList:
-            def __len__(self):
-                return 10;
-            def __getitem__(self, i):
-                if i > 2:
-                    raise IOError
-        self.assertRaises(IOError, self._write_test, BadList(), '')
-        class BadItem:
-            def __str__(self):
-                raise IOError
-        self.assertRaises(IOError, self._write_test, [BadItem()], '')
-
-    def test_write_bigfield(self):
-        # This exercises the buffer realloc functionality
-        bigstring = 'X' * 50000
-        self._write_test([bigstring,bigstring], '%s,%s' % \
-                         (bigstring, bigstring))
-
-    def test_write_quoting(self):
-        self._write_test(['a',1,'p,q'], 'a,1,"p,q"')
-        self.assertRaises(csv.Error,
-                          self._write_test,
-                          ['a',1,'p,q'], 'a,1,p,q',
-                          quoting = csv.QUOTE_NONE)
-        self._write_test(['a',1,'p,q'], 'a,1,"p,q"',
-                         quoting = csv.QUOTE_MINIMAL)
-        self._write_test(['a',1,'p,q'], '"a",1,"p,q"',
-                         quoting = csv.QUOTE_NONNUMERIC)
-        self._write_test(['a',1,'p,q'], '"a","1","p,q"',
-                         quoting = csv.QUOTE_ALL)
-        self._write_test(['a\nb',1], '"a\nb","1"',
-                         quoting = csv.QUOTE_ALL)
-
-    def test_write_escape(self):
-        self._write_test(['a',1,'p,q'], 'a,1,"p,q"',
-                         escapechar='\\')
-        self.assertRaises(csv.Error,
-                          self._write_test,
-                          ['a',1,'p,"q"'], 'a,1,"p,\\"q\\""',
-                          escapechar=None, doublequote=False)
-        self._write_test(['a',1,'p,"q"'], 'a,1,"p,\\"q\\""',
-                         escapechar='\\', doublequote = False)
-        self._write_test(['"'], '""""',
-                         escapechar='\\', quoting = csv.QUOTE_MINIMAL)
-        self._write_test(['"'], '\\"',
-                         escapechar='\\', quoting = csv.QUOTE_MINIMAL,
-                         doublequote = False)
-        self._write_test(['"'], '\\"',
-                         escapechar='\\', quoting = csv.QUOTE_NONE)
-        self._write_test(['a',1,'p,q'], 'a,1,p\\,q',
-                         escapechar='\\', quoting = csv.QUOTE_NONE)
-
-    def test_writerows(self):
-        class BrokenFile:
-            def write(self, buf):
-                raise IOError
-        writer = csv.writer(BrokenFile())
-        self.assertRaises(IOError, writer.writerows, [['a']])
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj)
-            self.assertRaises(TypeError, writer.writerows, None)
-            writer.writerows([['a','b'],['c','d']])
-            fileobj.seek(0)
-            self.assertEqual(fileobj.read(), "a,b\r\nc,d\r\n")
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython")
-    def test_write_float(self):
-        # Issue 13573: loss of precision because csv.writer
-        # uses str() for floats instead of repr()
-        orig_row = [1.234567890123, 1.0/7.0, 'abc']
-        f = StringIO()
-        c = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC)
-        c.writerow(orig_row)
-        f.seek(0)
-        c = csv.reader(f, quoting=csv.QUOTE_NONNUMERIC)
-        new_row = next(c)
-        self.assertEqual(orig_row, new_row)
-
-    def _read_test(self, input, expect, **kwargs):
-        reader = csv.reader(input, **kwargs)
-        result = list(reader)
-        self.assertEqual(result, expect)
-
-    def test_read_oddinputs(self):
-        self._read_test([], [])
-        self._read_test([''], [[]])
-        self.assertRaises(csv.Error, self._read_test,
-                          ['"ab"c'], None, strict = 1)
-        # cannot handle null bytes for the moment
-        self.assertRaises(csv.Error, self._read_test,
-                          ['ab\0c'], None, strict = 1)
-        self._read_test(['"ab"c'], [['abc']], doublequote = 0)
-
-    def test_read_eol(self):
-        self._read_test(['a,b'], [['a','b']])
-        self._read_test(['a,b\n'], [['a','b']])
-        self._read_test(['a,b\r\n'], [['a','b']])
-        self._read_test(['a,b\r'], [['a','b']])
-        self.assertRaises(csv.Error, self._read_test, ['a,b\rc,d'], [])
-        self.assertRaises(csv.Error, self._read_test, ['a,b\nc,d'], [])
-        self.assertRaises(csv.Error, self._read_test, ['a,b\r\nc,d'], [])
-
-    def test_read_escape(self):
-        self._read_test(['a,\\b,c'], [['a', 'b', 'c']], escapechar='\\')
-        self._read_test(['a,b\\,c'], [['a', 'b,c']], escapechar='\\')
-        self._read_test(['a,"b\\,c"'], [['a', 'b,c']], escapechar='\\')
-        self._read_test(['a,"b,\\c"'], [['a', 'b,c']], escapechar='\\')
-        self._read_test(['a,"b,c\\""'], [['a', 'b,c"']], escapechar='\\')
-        self._read_test(['a,"b,c"\\'], [['a', 'b,c\\']], escapechar='\\')
-
-    def test_read_quoting(self):
-        self._read_test(['1,",3,",5'], [['1', ',3,', '5']])
-        self._read_test(['1,",3,",5'], [['1', '"', '3', '"', '5']],
-                        quotechar=None, escapechar='\\')
-        self._read_test(['1,",3,",5'], [['1', '"', '3', '"', '5']],
-                        quoting=csv.QUOTE_NONE, escapechar='\\')
-        # will this fail where locale uses comma for decimals?
-        self._read_test([',3,"5",7.3, 9'], [['', 3, '5', 7.3, 9]],
-                        quoting=csv.QUOTE_NONNUMERIC)
-        self._read_test(['"a\nb", 7'], [['a\nb', ' 7']])
-        self.assertRaises(ValueError, self._read_test,
-                          ['abc,3'], [[]],
-                          quoting=csv.QUOTE_NONNUMERIC)
-
-    def test_read_bigfield(self):
-        # This exercises the buffer realloc functionality and field size
-        # limits.
-        limit = csv.field_size_limit()
-        try:
-            size = 50000
-            bigstring = 'X' * size
-            bigline = '%s,%s' % (bigstring, bigstring)
-            self._read_test([bigline], [[bigstring, bigstring]])
-            csv.field_size_limit(size)
-            self._read_test([bigline], [[bigstring, bigstring]])
-            self.assertEqual(csv.field_size_limit(), size)
-            csv.field_size_limit(size-1)
-            self.assertRaises(csv.Error, self._read_test, [bigline], [])
-            self.assertRaises(TypeError, csv.field_size_limit, None)
-            self.assertRaises(TypeError, csv.field_size_limit, 1, None)
-        finally:
-            csv.field_size_limit(limit)
-
-    def test_read_linenum(self):
-        for r in (csv.reader(['line,1', 'line,2', 'line,3']),
-                  csv.DictReader(['line,1', 'line,2', 'line,3'],
-                                 fieldnames=['a', 'b', 'c'])):
-            self.assertEqual(r.line_num, 0)
-            r.next()
-            self.assertEqual(r.line_num, 1)
-            r.next()
-            self.assertEqual(r.line_num, 2)
-            r.next()
-            self.assertEqual(r.line_num, 3)
-            self.assertRaises(StopIteration, r.next)
-            self.assertEqual(r.line_num, 3)
-
-    def test_roundtrip_quoteed_newlines(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj)
-            self.assertRaises(TypeError, writer.writerows, None)
-            rows = [['a\nb','b'],['c','x\r\nd']]
-            writer.writerows(rows)
-            fileobj.seek(0)
-            for i, row in enumerate(csv.reader(fileobj)):
-                self.assertEqual(row, rows[i])
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-class TestDialectRegistry(unittest.TestCase):
-    def test_registry_badargs(self):
-        self.assertRaises(TypeError, csv.list_dialects, None)
-        self.assertRaises(TypeError, csv.get_dialect)
-        self.assertRaises(csv.Error, csv.get_dialect, None)
-        self.assertRaises(csv.Error, csv.get_dialect, "nonesuch")
-        self.assertRaises(TypeError, csv.unregister_dialect)
-        self.assertRaises(csv.Error, csv.unregister_dialect, None)
-        self.assertRaises(csv.Error, csv.unregister_dialect, "nonesuch")
-        self.assertRaises(TypeError, csv.register_dialect, None)
-        self.assertRaises(TypeError, csv.register_dialect, None, None)
-        self.assertRaises(TypeError, csv.register_dialect, "nonesuch", 0, 0)
-        self.assertRaises(TypeError, csv.register_dialect, "nonesuch",
-                          badargument=None)
-        self.assertRaises(TypeError, csv.register_dialect, "nonesuch",
-                          quoting=None)
-        self.assertRaises(TypeError, csv.register_dialect, [])
-
-    def test_registry(self):
-        class myexceltsv(csv.excel):
-            delimiter = "\t"
-        name = "myexceltsv"
-        expected_dialects = csv.list_dialects() + [name]
-        expected_dialects.sort()
-        csv.register_dialect(name, myexceltsv)
-        self.addCleanup(csv.unregister_dialect, name)
-        self.assertEqual(csv.get_dialect(name).delimiter, '\t')
-        got_dialects = sorted(csv.list_dialects())
-        self.assertEqual(expected_dialects, got_dialects)
-
-    def test_register_kwargs(self):
-        name = 'fedcba'
-        csv.register_dialect(name, delimiter=';')
-        self.addCleanup(csv.unregister_dialect, name)
-        self.assertEqual(csv.get_dialect(name).delimiter, ';')
-        self.assertEqual([['X', 'Y', 'Z']], list(csv.reader(['X;Y;Z'], name)))
-
-    def test_incomplete_dialect(self):
-        class myexceltsv(csv.Dialect):
-            delimiter = "\t"
-        self.assertRaises(csv.Error, myexceltsv)
-
-    def test_space_dialect(self):
-        class space(csv.excel):
-            delimiter = " "
-            quoting = csv.QUOTE_NONE
-            escapechar = "\\"
-
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write("abc def\nc1ccccc1 benzene\n")
-            fileobj.seek(0)
-            rdr = csv.reader(fileobj, dialect=space())
-            self.assertEqual(rdr.next(), ["abc", "def"])
-            self.assertEqual(rdr.next(), ["c1ccccc1", "benzene"])
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_dialect_apply(self):
-        class testA(csv.excel):
-            delimiter = "\t"
-        class testB(csv.excel):
-            delimiter = ":"
-        class testC(csv.excel):
-            delimiter = "|"
-
-        csv.register_dialect('testC', testC)
-        try:
-            fd, name = tempfile.mkstemp()
-            fileobj = os.fdopen(fd, "w+b")
-            try:
-                writer = csv.writer(fileobj)
-                writer.writerow([1,2,3])
-                fileobj.seek(0)
-                self.assertEqual(fileobj.read(), "1,2,3\r\n")
-            finally:
-                fileobj.close()
-                os.unlink(name)
-
-            fd, name = tempfile.mkstemp()
-            fileobj = os.fdopen(fd, "w+b")
-            try:
-                writer = csv.writer(fileobj, testA)
-                writer.writerow([1,2,3])
-                fileobj.seek(0)
-                self.assertEqual(fileobj.read(), "1\t2\t3\r\n")
-            finally:
-                fileobj.close()
-                os.unlink(name)
-
-            fd, name = tempfile.mkstemp()
-            fileobj = os.fdopen(fd, "w+b")
-            try:
-                writer = csv.writer(fileobj, dialect=testB())
-                writer.writerow([1,2,3])
-                fileobj.seek(0)
-                self.assertEqual(fileobj.read(), "1:2:3\r\n")
-            finally:
-                fileobj.close()
-                os.unlink(name)
-
-            fd, name = tempfile.mkstemp()
-            fileobj = os.fdopen(fd, "w+b")
-            try:
-                writer = csv.writer(fileobj, dialect='testC')
-                writer.writerow([1,2,3])
-                fileobj.seek(0)
-                self.assertEqual(fileobj.read(), "1|2|3\r\n")
-            finally:
-                fileobj.close()
-                os.unlink(name)
-
-            fd, name = tempfile.mkstemp()
-            fileobj = os.fdopen(fd, "w+b")
-            try:
-                writer = csv.writer(fileobj, dialect=testA, delimiter=';')
-                writer.writerow([1,2,3])
-                fileobj.seek(0)
-                self.assertEqual(fileobj.read(), "1;2;3\r\n")
-            finally:
-                fileobj.close()
-                os.unlink(name)
-
-        finally:
-            csv.unregister_dialect('testC')
-
-    def test_bad_dialect(self):
-        # Unknown parameter
-        self.assertRaises(TypeError, csv.reader, [], bad_attr = 0)
-        # Bad values
-        self.assertRaises(TypeError, csv.reader, [], delimiter = None)
-        self.assertRaises(TypeError, csv.reader, [], quoting = -1)
-        self.assertRaises(TypeError, csv.reader, [], quoting = 100)
-
-class TestCsvBase(unittest.TestCase):
-    def readerAssertEqual(self, input, expected_result):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write(input)
-            fileobj.seek(0)
-            reader = csv.reader(fileobj, dialect = self.dialect)
-            fields = list(reader)
-            self.assertEqual(fields, expected_result)
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def writerAssertEqual(self, input, expected_result):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj, dialect = self.dialect)
-            writer.writerows(input)
-            fileobj.seek(0)
-            self.assertEqual(fileobj.read(), expected_result)
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-class TestDialectExcel(TestCsvBase):
-    dialect = 'excel'
-
-    def test_single(self):
-        self.readerAssertEqual('abc', [['abc']])
-
-    def test_simple(self):
-        self.readerAssertEqual('1,2,3,4,5', [['1','2','3','4','5']])
-
-    def test_blankline(self):
-        self.readerAssertEqual('', [])
-
-    def test_empty_fields(self):
-        self.readerAssertEqual(',', [['', '']])
-
-    def test_singlequoted(self):
-        self.readerAssertEqual('""', [['']])
-
-    def test_singlequoted_left_empty(self):
-        self.readerAssertEqual('"",', [['','']])
-
-    def test_singlequoted_right_empty(self):
-        self.readerAssertEqual(',""', [['','']])
-
-    def test_single_quoted_quote(self):
-        self.readerAssertEqual('""""', [['"']])
-
-    def test_quoted_quotes(self):
-        self.readerAssertEqual('""""""', [['""']])
-
-    def test_inline_quote(self):
-        self.readerAssertEqual('a""b', [['a""b']])
-
-    def test_inline_quotes(self):
-        self.readerAssertEqual('a"b"c', [['a"b"c']])
-
-    def test_quotes_and_more(self):
-        # Excel would never write a field containing '"a"b', but when
-        # reading one, it will return 'ab'.
-        self.readerAssertEqual('"a"b', [['ab']])
-
-    def test_lone_quote(self):
-        self.readerAssertEqual('a"b', [['a"b']])
-
-    def test_quote_and_quote(self):
-        # Excel would never write a field containing '"a" "b"', but when
-        # reading one, it will return 'a "b"'.
-        self.readerAssertEqual('"a" "b"', [['a "b"']])
-
-    def test_space_and_quote(self):
-        self.readerAssertEqual(' "a"', [[' "a"']])
-
-    def test_quoted(self):
-        self.readerAssertEqual('1,2,3,"I think, therefore I am",5,6',
-                               [['1', '2', '3',
-                                 'I think, therefore I am',
-                                 '5', '6']])
-
-    def test_quoted_quote(self):
-        self.readerAssertEqual('1,2,3,"""I see,"" said the blind man","as he picked up his hammer and saw"',
-                               [['1', '2', '3',
-                                 '"I see," said the blind man',
-                                 'as he picked up his hammer and saw']])
-
-    def test_quoted_nl(self):
-        input = '''\
-1,2,3,"""I see,""
-said the blind man","as he picked up his
-hammer and saw"
-9,8,7,6'''
-        self.readerAssertEqual(input,
-                               [['1', '2', '3',
-                                   '"I see,"\nsaid the blind man',
-                                   'as he picked up his\nhammer and saw'],
-                                ['9','8','7','6']])
-
-    def test_dubious_quote(self):
-        self.readerAssertEqual('12,12,1",', [['12', '12', '1"', '']])
-
-    def test_null(self):
-        self.writerAssertEqual([], '')
-
-    def test_single_writer(self):
-        self.writerAssertEqual([['abc']], 'abc\r\n')
-
-    def test_simple_writer(self):
-        self.writerAssertEqual([[1, 2, 'abc', 3, 4]], '1,2,abc,3,4\r\n')
-
-    def test_quotes(self):
-        self.writerAssertEqual([[1, 2, 'a"bc"', 3, 4]], '1,2,"a""bc""",3,4\r\n')
-
-    def test_quote_fieldsep(self):
-        self.writerAssertEqual([['abc,def']], '"abc,def"\r\n')
-
-    def test_newlines(self):
-        self.writerAssertEqual([[1, 2, 'a\nbc', 3, 4]], '1,2,"a\nbc",3,4\r\n')
-
-class EscapedExcel(csv.excel):
-    quoting = csv.QUOTE_NONE
-    escapechar = '\\'
-
-class TestEscapedExcel(TestCsvBase):
-    dialect = EscapedExcel()
-
-    def test_escape_fieldsep(self):
-        self.writerAssertEqual([['abc,def']], 'abc\\,def\r\n')
-
-    def test_read_escape_fieldsep(self):
-        self.readerAssertEqual('abc\\,def\r\n', [['abc,def']])
-
-class QuotedEscapedExcel(csv.excel):
-    quoting = csv.QUOTE_NONNUMERIC
-    escapechar = '\\'
-
-class TestQuotedEscapedExcel(TestCsvBase):
-    dialect = QuotedEscapedExcel()
-
-    def test_write_escape_fieldsep(self):
-        self.writerAssertEqual([['abc,def']], '"abc,def"\r\n')
-
-    def test_read_escape_fieldsep(self):
-        self.readerAssertEqual('"abc\\,def"\r\n', [['abc,def']])
-
-class TestDictFields(unittest.TestCase):
-    ### "long" means the row is longer than the number of fieldnames
-    ### "short" means there are fewer elements in the row than fieldnames
-
-    @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython")
-    def test_write_simple_dict(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = io.open(fd, 'w+b')
-        try:
-            writer = csv.DictWriter(fileobj, fieldnames = ["f1", "f2", "f3"])
-            writer.writeheader()
-            fileobj.seek(0)
-            self.assertEqual(fileobj.readline(), "f1,f2,f3\r\n")
-            writer.writerow({"f1": 10, "f3": "abc"})
-            fileobj.seek(0)
-            fileobj.readline() # header
-            self.assertEqual(fileobj.read(), "10,,abc\r\n")
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_write_no_fields(self):
-        fileobj = StringIO()
-        self.assertRaises(TypeError, csv.DictWriter, fileobj)
-
-    def test_read_dict_fields(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write("1,2,abc\r\n")
-            fileobj.seek(0)
-            reader = csv.DictReader(fileobj,
-                                    fieldnames=["f1", "f2", "f3"])
-            self.assertEqual(reader.next(), {"f1": '1', "f2": '2', "f3": 'abc'})
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_read_dict_no_fieldnames(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write("f1,f2,f3\r\n1,2,abc\r\n")
-            fileobj.seek(0)
-            reader = csv.DictReader(fileobj)
-            self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"])
-            self.assertEqual(reader.next(), {"f1": '1', "f2": '2', "f3": 'abc'})
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    # Two test cases to make sure existing ways of implicitly setting
-    # fieldnames continue to work.  Both arise from discussion in issue3436.
-    def test_read_dict_fieldnames_from_file(self):
-        fd, name = tempfile.mkstemp()
-        f = os.fdopen(fd, "w+b")
-        try:
-            f.write("f1,f2,f3\r\n1,2,abc\r\n")
-            f.seek(0)
-            reader = csv.DictReader(f, fieldnames=csv.reader(f).next())
-            self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"])
-            self.assertEqual(reader.next(), {"f1": '1', "f2": '2', "f3": 'abc'})
-        finally:
-            f.close()
-            os.unlink(name)
-
-    def test_read_dict_fieldnames_chain(self):
-        import itertools
-        fd, name = tempfile.mkstemp()
-        f = os.fdopen(fd, "w+b")
-        try:
-            f.write("f1,f2,f3\r\n1,2,abc\r\n")
-            f.seek(0)
-            reader = csv.DictReader(f)
-            first = next(reader)
-            for row in itertools.chain([first], reader):
-                self.assertEqual(reader.fieldnames, ["f1", "f2", "f3"])
-                self.assertEqual(row, {"f1": '1', "f2": '2', "f3": 'abc'})
-        finally:
-            f.close()
-            os.unlink(name)
-
-    def test_read_long(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write("1,2,abc,4,5,6\r\n")
-            fileobj.seek(0)
-            reader = csv.DictReader(fileobj,
-                                    fieldnames=["f1", "f2"])
-            self.assertEqual(reader.next(), {"f1": '1', "f2": '2',
-                                             None: ["abc", "4", "5", "6"]})
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_read_long_with_rest(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write("1,2,abc,4,5,6\r\n")
-            fileobj.seek(0)
-            reader = csv.DictReader(fileobj,
-                                    fieldnames=["f1", "f2"], restkey="_rest")
-            self.assertEqual(reader.next(), {"f1": '1', "f2": '2',
-                                             "_rest": ["abc", "4", "5", "6"]})
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_read_long_with_rest_no_fieldnames(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write("f1,f2\r\n1,2,abc,4,5,6\r\n")
-            fileobj.seek(0)
-            reader = csv.DictReader(fileobj, restkey="_rest")
-            self.assertEqual(reader.fieldnames, ["f1", "f2"])
-            self.assertEqual(reader.next(), {"f1": '1', "f2": '2',
-                                             "_rest": ["abc", "4", "5", "6"]})
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_read_short(self):
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            fileobj.write("1,2,abc,4,5,6\r\n1,2,abc\r\n")
-            fileobj.seek(0)
-            reader = csv.DictReader(fileobj,
-                                    fieldnames="1 2 3 4 5 6".split(),
-                                    restval="DEFAULT")
-            self.assertEqual(reader.next(), {"1": '1', "2": '2', "3": 'abc',
-                                             "4": '4', "5": '5', "6": '6'})
-            self.assertEqual(reader.next(), {"1": '1', "2": '2', "3": 'abc',
-                                             "4": 'DEFAULT', "5": 'DEFAULT',
-                                             "6": 'DEFAULT'})
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_read_multi(self):
-        sample = [
-            '2147483648,43.0e12,17,abc,def\r\n',
-            '147483648,43.0e2,17,abc,def\r\n',
-            '47483648,43.0,170,abc,def\r\n'
-            ]
-
-        reader = csv.DictReader(sample,
-                                fieldnames="i1 float i2 s1 s2".split())
-        self.assertEqual(reader.next(), {"i1": '2147483648',
-                                         "float": '43.0e12',
-                                         "i2": '17',
-                                         "s1": 'abc',
-                                         "s2": 'def'})
-
-    def test_read_with_blanks(self):
-        reader = csv.DictReader(["1,2,abc,4,5,6\r\n","\r\n",
-                                 "1,2,abc,4,5,6\r\n"],
-                                fieldnames="1 2 3 4 5 6".split())
-        self.assertEqual(reader.next(), {"1": '1', "2": '2', "3": 'abc',
-                                         "4": '4', "5": '5', "6": '6'})
-        self.assertEqual(reader.next(), {"1": '1', "2": '2', "3": 'abc',
-                                         "4": '4', "5": '5', "6": '6'})
-
-    def test_read_semi_sep(self):
-        reader = csv.DictReader(["1;2;abc;4;5;6\r\n"],
-                                fieldnames="1 2 3 4 5 6".split(),
-                                delimiter=';')
-        self.assertEqual(reader.next(), {"1": '1', "2": '2', "3": 'abc',
-                                         "4": '4', "5": '5', "6": '6'})
-
-class TestArrayWrites(unittest.TestCase):
-    def test_int_write(self):
-        import array
-        contents = [(20-i) for i in range(20)]
-        a = array.array('i', contents)
-
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj, dialect="excel")
-            writer.writerow(a)
-            expected = ",".join([str(i) for i in a])+"\r\n"
-            fileobj.seek(0)
-            self.assertEqual(fileobj.read(), expected)
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython")
-    def test_double_write(self):
-        import array
-        contents = [(20-i)*0.1 for i in range(20)]
-        a = array.array('d', contents)
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj, dialect="excel")
-            writer.writerow(a)
-            expected = ",".join([repr(i) for i in a])+"\r\n"
-            fileobj.seek(0)
-            self.assertEqual(fileobj.read(), expected)
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    @unittest.skipIf(test_support.is_jython, "FIXME: not working in Jython")
-    def test_float_write(self):
-        import array
-        contents = [(20-i)*0.1 for i in range(20)]
-        a = array.array('f', contents)
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj, dialect="excel")
-            writer.writerow(a)
-            expected = ",".join([repr(i) for i in a])+"\r\n"
-            fileobj.seek(0)
-            self.assertEqual(fileobj.read(), expected)
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-    def test_char_write(self):
-        import array, string
-        a = array.array('c', string.letters)
-        fd, name = tempfile.mkstemp()
-        fileobj = os.fdopen(fd, "w+b")
-        try:
-            writer = csv.writer(fileobj, dialect="excel")
-            writer.writerow(a)
-            expected = ",".join(a)+"\r\n"
-            fileobj.seek(0)
-            self.assertEqual(fileobj.read(), expected)
-        finally:
-            fileobj.close()
-            os.unlink(name)
-
-class TestDialectValidity(unittest.TestCase):
-    def test_quoting(self):
-        class mydialect(csv.Dialect):
-            delimiter = ";"
-            escapechar = '\\'
-            doublequote = False
-            skipinitialspace = True
-            lineterminator = '\r\n'
-            quoting = csv.QUOTE_NONE
-        d = mydialect()
-
-        mydialect.quoting = None
-        self.assertRaises(csv.Error, mydialect)
-
-        mydialect.doublequote = True
-        mydialect.quoting = csv.QUOTE_ALL
-        mydialect.quotechar = '"'
-        d = mydialect()
-
-        mydialect.quotechar = "''"
-        self.assertRaises(csv.Error, mydialect)
-
-        mydialect.quotechar = 4
-        self.assertRaises(csv.Error, mydialect)
-
-    def test_delimiter(self):
-        class mydialect(csv.Dialect):
-            delimiter = ";"
-            escapechar = '\\'
-            doublequote = False
-            skipinitialspace = True
-            lineterminator = '\r\n'
-            quoting = csv.QUOTE_NONE
-        d = mydialect()
-
-        mydialect.delimiter = ":::"
-        self.assertRaises(csv.Error, mydialect)
-
-        mydialect.delimiter = 4
-        self.assertRaises(csv.Error, mydialect)
-
-    def test_lineterminator(self):
-        class mydialect(csv.Dialect):
-            delimiter = ";"
-            escapechar = '\\'
-            doublequote = False
-            skipinitialspace = True
-            lineterminator = '\r\n'
-            quoting = csv.QUOTE_NONE
-        d = mydialect()
-
-        mydialect.lineterminator = ":::"
-        d = mydialect()
-
-        mydialect.lineterminator = 4
-        self.assertRaises(csv.Error, mydialect)
-
-
-class TestSniffer(unittest.TestCase):
-    sample1 = """\
-Harry's, Arlington Heights, IL, 2/1/03, Kimi Hayes
-Shark City, Glendale Heights, IL, 12/28/02, Prezence
-Tommy's Place, Blue Island, IL, 12/28/02, Blue Sunday/White Crow
-Stonecutters Seafood and Chop House, Lemont, IL, 12/19/02, Week Back
-"""
-    sample2 = """\
-'Harry''s':'Arlington Heights':'IL':'2/1/03':'Kimi Hayes'
-'Shark City':'Glendale Heights':'IL':'12/28/02':'Prezence'
-'Tommy''s Place':'Blue Island':'IL':'12/28/02':'Blue Sunday/White Crow'
-'Stonecutters ''Seafood'' and Chop House':'Lemont':'IL':'12/19/02':'Week Back'
-"""
-    header = '''\
-"venue","city","state","date","performers"
-'''
-    sample3 = '''\
-05/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03
-05/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03
-05/05/03?05/05/03?05/05/03?05/05/03?05/05/03?05/05/03
-'''
-
-    sample4 = '''\
-2147483648;43.0e12;17;abc;def
-147483648;43.0e2;17;abc;def
-47483648;43.0;170;abc;def
-'''
-
-    sample5 = "aaa\tbbb\r\nAAA\t\r\nBBB\t\r\n"
-    sample6 = "a|b|c\r\nd|e|f\r\n"
-    sample7 = "'a'|'b'|'c'\r\n'd'|e|f\r\n"
-
-    def test_has_header(self):
-        sniffer = csv.Sniffer()
-        self.assertEqual(sniffer.has_header(self.sample1), False)
-        self.assertEqual(sniffer.has_header(self.header+self.sample1), True)
-
-    def test_sniff(self):
-        sniffer = csv.Sniffer()
-        dialect = sniffer.sniff(self.sample1)
-        self.assertEqual(dialect.delimiter, ",")
-        self.assertEqual(dialect.quotechar, '"')
-        self.assertEqual(dialect.skipinitialspace, True)
-
-        dialect = sniffer.sniff(self.sample2)
-        self.assertEqual(dialect.delimiter, ":")
-        self.assertEqual(dialect.quotechar, "'")
-        self.assertEqual(dialect.skipinitialspace, False)
-
-    def test_delimiters(self):
-        sniffer = csv.Sniffer()
-        dialect = sniffer.sniff(self.sample3)
-        # given that all three lines in sample3 are equal,
-        # I think that any character could have been 'guessed' as the
-        # delimiter, depending on dictionary order
-        self.assertIn(dialect.delimiter, self.sample3)
-        dialect = sniffer.sniff(self.sample3, delimiters="?,")
-        self.assertEqual(dialect.delimiter, "?")
-        dialect = sniffer.sniff(self.sample3, delimiters="/,")
-        self.assertEqual(dialect.delimiter, "/")
-        dialect = sniffer.sniff(self.sample4)
-        self.assertEqual(dialect.delimiter, ";")
-        dialect = sniffer.sniff(self.sample5)
-        self.assertEqual(dialect.delimiter, "\t")
-        dialect = sniffer.sniff(self.sample6)
-        self.assertEqual(dialect.delimiter, "|")
-        dialect = sniffer.sniff(self.sample7)
-        self.assertEqual(dialect.delimiter, "|")
-        self.assertEqual(dialect.quotechar, "'")
-
-    def test_doublequote(self):
-        sniffer = csv.Sniffer()
-        dialect = sniffer.sniff(self.header)
-        self.assertFalse(dialect.doublequote)
-        dialect = sniffer.sniff(self.sample2)
-        self.assertTrue(dialect.doublequote)
-
-if not hasattr(sys, "gettotalrefcount"):
-    if test_support.verbose: print "*** skipping leakage tests ***"
-else:
-    class NUL:
-        def write(s, *args):
-            pass
-        writelines = write
-
-    class TestLeaks(unittest.TestCase):
-        def test_create_read(self):
-            delta = 0
-            lastrc = sys.gettotalrefcount()
-            for i in xrange(20):
-                gc.collect()
-                self.assertEqual(gc.garbage, [])
-                rc = sys.gettotalrefcount()
-                csv.reader(["a,b,c\r\n"])
-                csv.reader(["a,b,c\r\n"])
-                csv.reader(["a,b,c\r\n"])
-                delta = rc-lastrc
-                lastrc = rc
-            # if csv.reader() leaks, last delta should be 3 or more
-            self.assertEqual(delta < 3, True)
-
-        def test_create_write(self):
-            delta = 0
-            lastrc = sys.gettotalrefcount()
-            s = NUL()
-            for i in xrange(20):
-                gc.collect()
-                self.assertEqual(gc.garbage, [])
-                rc = sys.gettotalrefcount()
-                csv.writer(s)
-                csv.writer(s)
-                csv.writer(s)
-                delta = rc-lastrc
-                lastrc = rc
-            # if csv.writer() leaks, last delta should be 3 or more
-            self.assertEqual(delta < 3, True)
-
-        def test_read(self):
-            delta = 0
-            rows = ["a,b,c\r\n"]*5
-            lastrc = sys.gettotalrefcount()
-            for i in xrange(20):
-                gc.collect()
-                self.assertEqual(gc.garbage, [])
-                rc = sys.gettotalrefcount()
-                rdr = csv.reader(rows)
-                for row in rdr:
-                    pass
-                delta = rc-lastrc
-                lastrc = rc
-            # if reader leaks during read, delta should be 5 or more
-            self.assertEqual(delta < 5, True)
-
-        def test_write(self):
-            delta = 0
-            rows = [[1,2,3]]*5
-            s = NUL()
-            lastrc = sys.gettotalrefcount()
-            for i in xrange(20):
-                gc.collect()
-                self.assertEqual(gc.garbage, [])
-                rc = sys.gettotalrefcount()
-                writer = csv.writer(s)
-                for row in rows:
-                    writer.writerow(row)
-                delta = rc-lastrc
-                lastrc = rc
-            # if writer leaks during write, last delta should be 5 or more
-            self.assertEqual(delta < 5, True)
-
-# commented out for now - csv module doesn't yet support Unicode
-## class TestUnicode(unittest.TestCase):
-##     def test_unicode_read(self):
-##         import codecs
-##         f = codecs.EncodedFile(StringIO("Martin von Löwis,"
-##                                         "Marc André Lemburg,"
-##                                         "Guido van Rossum,"
-##                                         "François Pinard\r\n"),
-##                                data_encoding='iso-8859-1')
-##         reader = csv.reader(f)
-##         self.assertEqual(list(reader), [[u"Martin von Löwis",
-##                                          u"Marc André Lemburg",
-##                                          u"Guido van Rossum",
-##                                          u"François Pinardn"]])
-
-def test_main():
-    mod = sys.modules[__name__]
-    test_support.run_unittest(
-        *[getattr(mod, name) for name in dir(mod) if name.startswith('Test')]
-    )
-
-if __name__ == '__main__':
-    test_main()
diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -1449,6 +1449,18 @@
         self.assertEqual(float(d1), 66)
         self.assertEqual(float(d2), 15.32)
 
+    def test_nan_to_float(self):
+        # Test conversions of decimal NANs to float.
+        # See http://bugs.python.org/issue15544
+        for s in ('nan', 'nan1234', '-nan', '-nan2468'):
+            f = float(Decimal(s))
+            self.assertTrue(math.isnan(f))
+
+    def test_snan_to_float(self):
+        for s in ('snan', '-snan', 'snan1357', '-snan1234'):
+            d = Decimal(s)
+            self.assertRaises(ValueError, float, d)
+
     def test_eval_round_trip(self):
 
         #with zero
diff --git a/Lib/test/test_dict_jy.py b/Lib/test/test_dict_jy.py
--- a/Lib/test/test_dict_jy.py
+++ b/Lib/test/test_dict_jy.py
@@ -1,6 +1,7 @@
 from test import test_support
 import java
 import unittest
+from collections import defaultdict
 
 class DictInitTest(unittest.TestCase):
     def testInternalSetitemInInit(self):
@@ -93,6 +94,12 @@
             def __getitem__(self, key):
                 raise CustomKeyError("custom message")
         self.assertRaises(CustomKeyError, lambda: DerivedDict()['foo'])
+    
+    def test_issue1676(self):
+        #See http://bugs.jython.org/issue1676
+        x=defaultdict()
+        #This formerly caused an NPE.
+        self.assertEqual(None, x.pop(None,None))
 
 class JavaIntegrationTest(unittest.TestCase):
     "Tests for instantiating dicts from Java maps and hashtables"
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
--- a/Lib/test/test_httpservers.py
+++ b/Lib/test/test_httpservers.py
@@ -4,11 +4,6 @@
 Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
 """
 
-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
-from SimpleHTTPServer import SimpleHTTPRequestHandler
-from CGIHTTPServer import CGIHTTPRequestHandler
-import CGIHTTPServer
-
 import os
 import sys
 import re
@@ -17,12 +12,17 @@
 import urllib
 import httplib
 import tempfile
+import unittest
+import CGIHTTPServer
 
-import unittest
 
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from CGIHTTPServer import CGIHTTPRequestHandler
 from StringIO import StringIO
+from test import test_support
 
-from test import test_support
+
 threading = test_support.import_module('threading')
 
 
@@ -43,7 +43,7 @@
         self.end_headers()
         self.wfile.write(b'<html><body>Data</body></html>\r\n')
 
-    def log_message(self, format, *args):
+    def log_message(self, fmt, *args):
         pass
 
 
@@ -97,9 +97,9 @@
         self.handler = SocketlessRequestHandler()
 
     def send_typical_request(self, message):
-        input = StringIO(message)
+        input_msg = StringIO(message)
         output = StringIO()
-        self.handler.rfile = input
+        self.handler.rfile = input_msg
         self.handler.wfile = output
         self.handler.handle_one_request()
         output.seek(0)
@@ -296,7 +296,7 @@
             os.chdir(self.cwd)
             try:
                 shutil.rmtree(self.tempdir)
-            except:
+            except OSError:
                 pass
         finally:
             BaseTestCase.tearDown(self)
@@ -418,42 +418,44 @@
         finally:
             BaseTestCase.tearDown(self)
 
-    def test_url_collapse_path_split(self):
+    def test_url_collapse_path(self):
+        # verify tail is the last portion and head is the rest on proper urls
         test_vectors = {
-            '': ('/', ''),
+            '': '//',
             '..': IndexError,
             '/.//..': IndexError,
-            '/': ('/', ''),
-            '//': ('/', ''),
-            '/\\': ('/', '\\'),
-            '/.//': ('/', ''),
-            'cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
-            '/cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
-            '/cgi-bin/file1.py/PATH-INFO': ('/cgi-bin', 'file1.py/PATH-INFO'),
-            'a': ('/', 'a'),
-            '/a': ('/', 'a'),
-            '//a': ('/', 'a'),
-            './a': ('/', 'a'),
-            './C:/': ('/C:', ''),
-            '/a/b': ('/a', 'b'),
-            '/a/b/': ('/a/b', ''),
-            '/a/b/c/..': ('/a/b', ''),
-            '/a/b/c/../d': ('/a/b', 'd'),
-            '/a/b/c/../d/e/../f': ('/a/b/d', 'f'),
-            '/a/b/c/../d/e/../../f': ('/a/b', 'f'),
-            '/a/b/c/../d/e/.././././..//f': ('/a/b', 'f'),
+            '/': '//',
+            '//': '//',
+            '/\\': '//\\',
+            '/.//': '//',
+            'cgi-bin/file1.py': '/cgi-bin/file1.py',
+            '/cgi-bin/file1.py': '/cgi-bin/file1.py',
+            'a': '//a',
+            '/a': '//a',
+            '//a': '//a',
+            './a': '//a',
+            './C:/': '/C:/',
+            '/a/b': '/a/b',
+            '/a/b/': '/a/b/',
+            '/a/b/.': '/a/b/',
+            '/a/b/c/..': '/a/b/',
+            '/a/b/c/../d': '/a/b/d',
+            '/a/b/c/../d/e/../f': '/a/b/d/f',
+            '/a/b/c/../d/e/../../f': '/a/b/f',
+            '/a/b/c/../d/e/.././././..//f': '/a/b/f',
             '../a/b/c/../d/e/.././././..//f': IndexError,
-            '/a/b/c/../d/e/../../../f': ('/a', 'f'),
-            '/a/b/c/../d/e/../../../../f': ('/', 'f'),
+            '/a/b/c/../d/e/../../../f': '/a/f',
+            '/a/b/c/../d/e/../../../../f': '//f',
             '/a/b/c/../d/e/../../../../../f': IndexError,
-            '/a/b/c/../d/e/../../../../f/..': ('/', ''),
+            '/a/b/c/../d/e/../../../../f/..': '//',
+            '/a/b/c/../d/e/../../../../f/../.': '//',
         }
         for path, expected in test_vectors.iteritems():
             if isinstance(expected, type) and issubclass(expected, Exception):
                 self.assertRaises(expected,
-                                  CGIHTTPServer._url_collapse_path_split, path)
+                                  CGIHTTPServer._url_collapse_path, path)
             else:
-                actual = CGIHTTPServer._url_collapse_path_split(path)
+                actual = CGIHTTPServer._url_collapse_path(path)
                 self.assertEqual(expected, actual,
                                  msg='path = %r\nGot:    %r\nWanted: %r' %
                                  (path, actual, expected))
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -634,6 +634,23 @@
         "len(array.array) returns number of elements rather than bytelength"
     )(IOTest.test_array_writes)
 
+    # When Jython tries to use UnsupportedOperation as _pyio defines it, it runs
+    # into a problem with multiple inheritance and the slots array: issue 1996.
+    # Override the affected test version just so we can skip it visibly.
+    @unittest.skipIf(support.is_jython, "FIXME: Jython issue 1996")
+    def test_invalid_operations(self):
+        pass
+
+    # Jython does not use integer file descriptors but an object instead.
+    # Unfortunately, _pyio.open checks that it is an int.
+    # Override the affected test versions just so we can skip them visibly.
+    @unittest.skipIf(support.is_jython, "Jython does not use integer file descriptors")
+    def test_closefd_attr(self):
+        pass
+    @unittest.skipIf(support.is_jython, "Jython does not use integer file descriptors")
+    def test_read_closed(self):
+        pass
+
 
 class CommonBufferedTests:
     # Tests common to BufferedReader, BufferedWriter and BufferedRandom
@@ -1371,6 +1388,13 @@
 class PyBufferedRWPairTest(BufferedRWPairTest):
     tp = pyio.BufferedRWPair
 
+    # When Jython tries to use UnsupportedOperation as _pyio defines it, it runs
+    # into a problem with multiple inheritance and the slots array: issue 1996.
+    # Override the affected test version just so we can skip it visibly.
+    @unittest.skipIf(support.is_jython, "FIXME: Jython issue 1996")
+    def test_detach(self):
+        pass
+
 
 class BufferedRandomTest(BufferedReaderTest, BufferedWriterTest):
     read_mode = "rb+"
@@ -2552,8 +2576,8 @@
         self.assertEqual(g.raw.mode, "wb")
         self.assertEqual(g.name,     f.fileno())
         self.assertEqual(g.raw.name, f.fileno())
-        f.close()
-        g.close()
+        g.close()   # Jython difference: close g first (which may flush) ...
+        f.close()   # Jython difference: then close f, which closes the fd
 
     def test_io_after_close(self):
         for kwargs in [
@@ -2719,10 +2743,23 @@
 class PyMiscIOTest(MiscIOTest):
     io = pyio
 
-
- at unittest.skipIf(os.name == 'nt' or 
-                 (sys.platform[:4] == 'java' and os._name == 'nt'),
-                 'POSIX signals required for this test.')
+    # When Jython tries to use UnsupportedOperation as _pyio defines it, it runs
+    # into a problem with multiple inheritance and the slots array: issue 1996.
+    # Override the affected test version just so we can skip it visibly.
+    @unittest.skipIf(support.is_jython, "FIXME: Jython issue 1996")
+    def test_io_after_close(self):
+        pass
+
+    # Jython does not use integer file descriptors but an object instead.
+    # Unfortunately, _pyio.open checks that it is an int.
+    # Override the affected test version just so we can skip it visibly.
+    @unittest.skipIf(support.is_jython, "Jython does not use integer file descriptors")
+    def test_attributes(self):
+        pass
+
+
+ at unittest.skipIf(support.is_jython, "Jython does not support os.pipe()")
+ at unittest.skipIf(os.name == 'nt', 'POSIX signals required for this test.')
 class SignalsTest(unittest.TestCase):
 
     def setUp(self):
diff --git a/Lib/test/test_io_jy.py b/Lib/test/test_io_jy.py
deleted file mode 100644
--- a/Lib/test/test_io_jy.py
+++ /dev/null
@@ -1,66 +0,0 @@
-"""Misc io tests.
-
-Made for Jython.
-"""
-import unittest
-
-from org.python.core.util import FileUtil
-from org.python.core.io import StreamIO
-
-from java.io import InputStream
-from java.nio import ByteBuffer;
-
-class InfiniteInputStream(InputStream):
-
-    def read(self, *args):
-        if len(args) == 0:
-            return ord('x')
-        elif len(args) == 1:
-            return InputStream.read(self, args[0])
-        else:
-            return self.read_buffer(*args)
-
-    def read_buffer(self, buf, off, length):
-        if length > 0:
-            buf[off] = ord('x')
-            return 1
-        return 0
-
-
-class IoTestCase(unittest.TestCase):
-    """
-    Jython was failing to read all available content when an InputStream
-    returns early. Java's InputStream.read() is allowed to return less than the
-    requested # of bytes under non-exceptional/EOF conditions, whereas
-    (for example) wsgi.input requires the file.read() method to block until the
-    requested # of bytes are available (except for exceptional/EOF conditions).
-
-    See http://bugs.jython.org/issue1754 for more discussion.
-    """
-    def test_infinite_input(self):
-        iis = InfiniteInputStream()
-        f = FileUtil.wrap(iis, 'rb')
-        size = 10000
-        self.assertEqual(len(f.read(size)), size)
-        self.assertEqual(len(f.read(size)), size)
-        self.assertEqual(len(f.read(size)), size)
-
-    def test_buffer_no_array(self):
-        """
-        Directly tests StreamIO with and without a backing array and an
-        InputStream that returns early.
-        """
-        size = 10000
-        without_array = ByteBuffer.allocateDirect(size)
-        self.assertFalse(without_array.hasArray())
-        with_array = ByteBuffer.allocate(size)
-        self.assertTrue(with_array.hasArray())
-        bbs = [with_array, without_array]
-        for bb in bbs:
-            iis = InfiniteInputStream()
-            io = StreamIO(iis, True)
-            self.assertEqual(io.readinto(bb), size)
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py
--- a/Lib/test/test_mailbox.py
+++ b/Lib/test/test_mailbox.py
@@ -8,6 +8,7 @@
 import re
 import shutil
 import StringIO
+import tempfile
 from test import test_support
 import unittest
 import mailbox
@@ -20,7 +21,7 @@
 # Silence Py3k warning
 rfc822 = test_support.import_module('rfc822', deprecated=True)
 
-class TestBase(unittest.TestCase):
+class TestBase:
 
     def _check_sample(self, msg):
         # Inspect a mailbox.Message representation of the sample message
@@ -39,15 +40,15 @@
     def _delete_recursively(self, target):
         # Delete a file or delete a directory recursively
         if os.path.isdir(target):
-            shutil.rmtree(target)
+            test_support.rmtree(target)
         elif os.path.exists(target):
-            os.remove(target)
+            test_support.unlink(target)
 
 
 class TestMailbox(TestBase):
 
     _factory = None     # Overridden by subclasses to reuse tests
-    _template = 'From: foo\n\n%s'
+    _template = 'From: foo\n\n%s\n'
 
     def setUp(self):
         self._path = test_support.TESTFN
@@ -75,6 +76,18 @@
         for i in (1, 2, 3, 4):
             self._check_sample(self._box[keys[i]])
 
+    def test_add_file(self):
+        with tempfile.TemporaryFile('w+') as f:
+            f.write(_sample_message)
+            f.seek(0)
+            key = self._box.add(f)
+        self.assertEqual(self._box.get_string(key).split('\n'),
+            _sample_message.split('\n'))
+
+    def test_add_StringIO(self):
+        key = self._box.add(StringIO.StringIO(self._template % "0"))
+        self.assertEqual(self._box.get_string(key), self._template % "0")
+
     def test_remove(self):
         # Remove messages using remove()
         self._test_remove_or_delitem(self._box.remove)
@@ -124,7 +137,7 @@
         key0 = self._box.add(self._template % 0)
         msg = self._box.get(key0)
         self.assertEqual(msg['from'], 'foo')
-        self.assertEqual(msg.get_payload(), '0')
+        self.assertEqual(msg.get_payload(), '0\n')
         self.assertIs(self._box.get('foo'), None)
         self.assertFalse(self._box.get('foo', False))
         self._box.close()
@@ -132,14 +145,15 @@
         key1 = self._box.add(self._template % 1)
         msg = self._box.get(key1)
         self.assertEqual(msg['from'], 'foo')
-        self.assertEqual(msg.fp.read(), '1')
+        self.assertEqual(msg.fp.read(), '1' + os.linesep)
+        msg.fp.close()
 
     def test_getitem(self):
         # Retrieve message using __getitem__()
         key0 = self._box.add(self._template % 0)
         msg = self._box[key0]
         self.assertEqual(msg['from'], 'foo')
-        self.assertEqual(msg.get_payload(), '0')
+        self.assertEqual(msg.get_payload(), '0\n')
         self.assertRaises(KeyError, lambda: self._box['foo'])
         self._box.discard(key0)
         self.assertRaises(KeyError, lambda: self._box[key0])
@@ -151,7 +165,7 @@
         msg0 = self._box.get_message(key0)
         self.assertIsInstance(msg0, mailbox.Message)
         self.assertEqual(msg0['from'], 'foo')
-        self.assertEqual(msg0.get_payload(), '0')
+        self.assertEqual(msg0.get_payload(), '0\n')
         self._check_sample(self._box.get_message(key1))
 
     def test_get_string(self):
@@ -165,10 +179,14 @@
         # Get file representations of messages
         key0 = self._box.add(self._template % 0)
         key1 = self._box.add(_sample_message)
-        self.assertEqual(self._box.get_file(key0).read().replace(os.linesep, '\n'),
+        msg0 = self._box.get_file(key0)
+        self.assertEqual(msg0.read().replace(os.linesep, '\n'),
                          self._template % 0)
-        self.assertEqual(self._box.get_file(key1).read().replace(os.linesep, '\n'),
+        msg1 = self._box.get_file(key1)
+        self.assertEqual(msg1.read().replace(os.linesep, '\n'),
                          _sample_message)
+        msg0.close()
+        msg1.close()
 
     def test_get_file_can_be_closed_twice(self):
         # Issue 11700
@@ -320,15 +338,15 @@
         self.assertIn(key0, self._box)
         key1 = self._box.add(self._template % 1)
         self.assertIn(key1, self._box)
-        self.assertEqual(self._box.pop(key0).get_payload(), '0')
+        self.assertEqual(self._box.pop(key0).get_payload(), '0\n')
         self.assertNotIn(key0, self._box)
         self.assertIn(key1, self._box)
         key2 = self._box.add(self._template % 2)
         self.assertIn(key2, self._box)
-        self.assertEqual(self._box.pop(key2).get_payload(), '2')
+        self.assertEqual(self._box.pop(key2).get_payload(), '2\n')
         self.assertNotIn(key2, self._box)
         self.assertIn(key1, self._box)
-        self.assertEqual(self._box.pop(key1).get_payload(), '1')
+        self.assertEqual(self._box.pop(key1).get_payload(), '1\n')
         self.assertNotIn(key1, self._box)
         self.assertEqual(len(self._box), 0)
 
@@ -386,6 +404,17 @@
         # Write changes to disk
         self._test_flush_or_close(self._box.flush, True)
 
+    def test_popitem_and_flush_twice(self):
+        # See #15036.
+        self._box.add(self._template % 0)
+        self._box.add(self._template % 1)
+        self._box.flush()
+
+        self._box.popitem()
+        self._box.flush()
+        self._box.popitem()
+        self._box.flush()
+
     def test_lock_unlock(self):
         # Lock and unlock the mailbox
         self.assertFalse(os.path.exists(self._get_lock_path()))
@@ -403,6 +432,7 @@
         self._box.add(contents[0])
         self._box.add(contents[1])
         self._box.add(contents[2])
+        oldbox = self._box
         method()
         if should_call_close:
             self._box.close()
@@ -411,6 +441,7 @@
         self.assertEqual(len(keys), 3)
         for key in keys:
             self.assertIn(self._box.get_string(key), contents)
+        oldbox.close()
 
     def test_dump_message(self):
         # Write message representations to disk
@@ -429,7 +460,7 @@
         return self._path + '.lock'
 
 
-class TestMailboxSuperclass(TestBase):
+class TestMailboxSuperclass(TestBase, unittest.TestCase):
 
     def test_notimplemented(self):
         # Test that all Mailbox methods raise NotImplementedException.
@@ -464,7 +495,7 @@
         self.assertRaises(NotImplementedError, lambda: box.close())
 
 
-class TestMaildir(TestMailbox):
+class TestMaildir(TestMailbox, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.Maildir(path, factory)
 
@@ -506,7 +537,7 @@
         msg_returned = self._box.get_message(key)
         self.assertEqual(msg_returned.get_subdir(), 'new')
         self.assertEqual(msg_returned.get_flags(), '')
-        self.assertEqual(msg_returned.get_payload(), '1')
+        self.assertEqual(msg_returned.get_payload(), '1\n')
         msg2 = mailbox.MaildirMessage(self._template % 2)
         msg2.set_info('2,S')
         self._box[key] = msg2
@@ -514,7 +545,7 @@
         msg_returned = self._box.get_message(key)
         self.assertEqual(msg_returned.get_subdir(), 'new')
         self.assertEqual(msg_returned.get_flags(), 'S')
-        self.assertEqual(msg_returned.get_payload(), '3')
+        self.assertEqual(msg_returned.get_payload(), '3\n')
 
     def test_consistent_factory(self):
         # Add a message.
@@ -636,13 +667,13 @@
             self.assertTrue(match is not None, "Invalid file name: '%s'" % tail)
             groups = match.groups()
             if previous_groups is not None:
-                self.assertTrue(int(groups[0] >= previous_groups[0]),
+                self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]),
                              "Non-monotonic seconds: '%s' before '%s'" %
                              (previous_groups[0], groups[0]))
-                self.assertTrue(int(groups[1] >= previous_groups[1]) or
-                             groups[0] != groups[1],
-                             "Non-monotonic milliseconds: '%s' before '%s'" %
-                             (previous_groups[1], groups[1]))
+                if int(groups[0]) == int(previous_groups[0]):
+                    self.assertGreaterEqual(int(groups[1]), int(previous_groups[1]),
+                                "Non-monotonic milliseconds: '%s' before '%s'" %
+                                (previous_groups[1], groups[1]))
                 self.assertTrue(int(groups[2]) == pid,
                              "Process ID mismatch: '%s' should be '%s'" %
                              (groups[2], pid))
@@ -813,7 +844,49 @@
         self._box._refresh()
         self.assertTrue(refreshed())
 
-class _TestMboxMMDF(TestMailbox):
+
+class _TestSingleFile(TestMailbox):
+    '''Common tests for single-file mailboxes'''
+
+    def test_add_doesnt_rewrite(self):
+        # When only adding messages, flush() should not rewrite the
+        # mailbox file. See issue #9559.
+
+        # Inode number changes if the contents are written to another
+        # file which is then renamed over the original file. So we
+        # must check that the inode number doesn't change.
+        inode_before = os.stat(self._path).st_ino
+
+        self._box.add(self._template % 0)
+        self._box.flush()
+
+        inode_after = os.stat(self._path).st_ino
+        self.assertEqual(inode_before, inode_after)
+
+        # Make sure the message was really added
+        self._box.close()
+        self._box = self._factory(self._path)
+        self.assertEqual(len(self._box), 1)
+
+    def test_permissions_after_flush(self):
+        # See issue #5346
+
+        # Make the mailbox world writable. It's unlikely that the new
+        # mailbox file would have these permissions after flush(),
+        # because umask usually prevents it.
+        mode = os.stat(self._path).st_mode | 0o666
+        os.chmod(self._path, mode)
+
+        self._box.add(self._template % 0)
+        i = self._box.add(self._template % 1)
+        # Need to remove one message to make flush() create a new file
+        self._box.remove(i)
+        self._box.flush()
+
+        self.assertEqual(os.stat(self._path).st_mode, mode)
+
+
+class _TestMboxMMDF(_TestSingleFile):
 
     def tearDown(self):
         self._box.close()
@@ -823,14 +896,14 @@
 
     def test_add_from_string(self):
         # Add a string starting with 'From ' to the mailbox
-        key = self._box.add('From foo at bar blah\nFrom: foo\n\n0')
+        key = self._box.add('From foo at bar blah\nFrom: foo\n\n0\n')
         self.assertEqual(self._box[key].get_from(), 'foo at bar blah')
-        self.assertEqual(self._box[key].get_payload(), '0')
+        self.assertEqual(self._box[key].get_payload(), '0\n')
 
     def test_add_mbox_or_mmdf_message(self):
         # Add an mboxMessage or MMDFMessage
         for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
-            msg = class_('From foo at bar blah\nFrom: foo\n\n0')
+            msg = class_('From foo at bar blah\nFrom: foo\n\n0\n')
             key = self._box.add(msg)
 
     def test_open_close_open(self):
@@ -914,7 +987,7 @@
         self._box.close()
 
 
-class TestMbox(_TestMboxMMDF):
+class TestMbox(_TestMboxMMDF, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.mbox(path, factory)
 
@@ -937,12 +1010,35 @@
             perms = st.st_mode
             self.assertFalse((perms & 0111)) # Execute bits should all be off.
 
-class TestMMDF(_TestMboxMMDF):
+    def test_terminating_newline(self):
+        message = email.message.Message()
+        message['From'] = 'john at example.com'
+        message.set_payload('No newline at the end')
+        i = self._box.add(message)
+
+        # A newline should have been appended to the payload
+        message = self._box.get(i)
+        self.assertEqual(message.get_payload(), 'No newline at the end\n')
+
+    def test_message_separator(self):
+        # Check there's always a single blank line after each message
+        self._box.add('From: foo\n\n0')  # No newline at the end
+        with open(self._path) as f:
+            data = f.read()
+            self.assertEqual(data[-3:], '0\n\n')
+
+        self._box.add('From: foo\n\n0\n')  # Newline at the end
+        with open(self._path) as f:
+            data = f.read()
+            self.assertEqual(data[-3:], '0\n\n')
+
+
+class TestMMDF(_TestMboxMMDF, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.MMDF(path, factory)
 
 
-class TestMH(TestMailbox):
+class TestMH(TestMailbox, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.MH(path, factory)
 
@@ -1074,7 +1170,7 @@
         return os.path.join(self._path, '.mh_sequences.lock')
 
 
-class TestBabyl(TestMailbox):
+class TestBabyl(_TestSingleFile, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.Babyl(path, factory)
 
@@ -1103,7 +1199,7 @@
         self.assertEqual(set(self._box.get_labels()), set(['blah']))
 
 
-class TestMessage(TestBase):
+class TestMessage(TestBase, unittest.TestCase):
 
     _factory = mailbox.Message      # Overridden by subclasses to reuse tests
 
@@ -1174,7 +1270,7 @@
         pass
 
 
-class TestMaildirMessage(TestMessage):
+class TestMaildirMessage(TestMessage, unittest.TestCase):
 
     _factory = mailbox.MaildirMessage
 
@@ -1249,7 +1345,7 @@
         self._check_sample(msg)
 
 
-class _TestMboxMMDFMessage(TestMessage):
+class _TestMboxMMDFMessage:
 
     _factory = mailbox._mboxMMDFMessage
 
@@ -1296,12 +1392,12 @@
                                  r"\d{2} \d{4}", msg.get_from()))
 
 
-class TestMboxMessage(_TestMboxMMDFMessage):
+class TestMboxMessage(_TestMboxMMDFMessage, TestMessage):
 
     _factory = mailbox.mboxMessage
 
 
-class TestMHMessage(TestMessage):
+class TestMHMessage(TestMessage, unittest.TestCase):
 
     _factory = mailbox.MHMessage
 
@@ -1332,7 +1428,7 @@
         self.assertEqual(msg.get_sequences(), ['foobar', 'replied'])
 
 
-class TestBabylMessage(TestMessage):
+class TestBabylMessage(TestMessage, unittest.TestCase):
 
     _factory = mailbox.BabylMessage
 
@@ -1387,12 +1483,12 @@
             self.assertEqual(visible[header], msg[header])
 
 
-class TestMMDFMessage(_TestMboxMMDFMessage):
+class TestMMDFMessage(_TestMboxMMDFMessage, TestMessage):
 
     _factory = mailbox.MMDFMessage
 
 
-class TestMessageConversion(TestBase):
+class TestMessageConversion(TestBase, unittest.TestCase):
 
     def test_plain_to_x(self):
         # Convert Message to all formats
@@ -1715,7 +1811,7 @@
         proxy.close()
 
 
-class TestProxyFile(TestProxyFileBase):
+class TestProxyFile(TestProxyFileBase, unittest.TestCase):
 
     def setUp(self):
         self._path = test_support.TESTFN
@@ -1764,7 +1860,7 @@
         self._test_close(mailbox._ProxyFile(self._file))
 
 
-class TestPartialFile(TestProxyFileBase):
+class TestPartialFile(TestProxyFileBase, unittest.TestCase):
 
     def setUp(self):
         self._path = test_support.TESTFN
@@ -1831,6 +1927,10 @@
     def setUp(self):
         # create a new maildir mailbox to work with:
         self._dir = test_support.TESTFN
+        if os.path.isdir(self._dir):
+            test_support.rmtree(self._dir)
+        if os.path.isfile(self._dir):
+            test_support.unlink(self._dir)
         os.mkdir(self._dir)
         os.mkdir(os.path.join(self._dir, "cur"))
         os.mkdir(os.path.join(self._dir, "tmp"))
@@ -1840,10 +1940,10 @@
 
     def tearDown(self):
         map(os.unlink, self._msgfiles)
-        os.rmdir(os.path.join(self._dir, "cur"))
-        os.rmdir(os.path.join(self._dir, "tmp"))
-        os.rmdir(os.path.join(self._dir, "new"))
-        os.rmdir(self._dir)
+        test_support.rmdir(os.path.join(self._dir, "cur"))
+        test_support.rmdir(os.path.join(self._dir, "tmp"))
+        test_support.rmdir(os.path.join(self._dir, "new"))
+        test_support.rmdir(self._dir)
 
     def createMessage(self, dir, mbox=False):
         t = int(time.time() % 1000000)
@@ -1879,7 +1979,9 @@
         self.createMessage("cur")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         #self.assertTrue(len(self.mbox.boxes) == 1)
-        self.assertIsNot(self.mbox.next(), None)
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
         self.assertIs(self.mbox.next(), None)
         self.assertIs(self.mbox.next(), None)
 
@@ -1887,7 +1989,9 @@
         self.createMessage("new")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         #self.assertTrue(len(self.mbox.boxes) == 1)
-        self.assertIsNot(self.mbox.next(), None)
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
         self.assertIs(self.mbox.next(), None)
         self.assertIs(self.mbox.next(), None)
 
@@ -1896,8 +2000,12 @@
         self.createMessage("new")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         #self.assertTrue(len(self.mbox.boxes) == 2)
-        self.assertIsNot(self.mbox.next(), None)
-        self.assertIsNot(self.mbox.next(), None)
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
         self.assertIs(self.mbox.next(), None)
         self.assertIs(self.mbox.next(), None)
 
@@ -1906,11 +2014,13 @@
         import email.parser
         fname = self.createMessage("cur", True)
         n = 0
-        for msg in mailbox.PortableUnixMailbox(open(fname),
+        fid = open(fname)
+        for msg in mailbox.PortableUnixMailbox(fid,
                                                email.parser.Parser().parse):
             n += 1
             self.assertEqual(msg["subject"], "Simple Test")
             self.assertEqual(len(str(msg)), len(FROM_)+len(DUMMY_MESSAGE))
+        fid.close()
         self.assertEqual(n, 1)
 
 ## End: classes from the original module (for backward compatibility).
diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_memoryio.py
@@ -0,0 +1,770 @@
+"""Unit tests for memory-based file-like objects.
+StringIO -- for unicode strings
+BytesIO -- for bytes
+"""
+
+from __future__ import unicode_literals
+from __future__ import print_function
+
+import unittest
+from test import test_support as support
+
+import io
+import _pyio as pyio
+import pickle
+
+class MemorySeekTestMixin:
+
+    def testInit(self):
+        buf = self.buftype("1234567890")
+        bytesIo = self.ioclass(buf)
+
+    def testRead(self):
+        buf = self.buftype("1234567890")
+        bytesIo = self.ioclass(buf)
+
+        self.assertEqual(buf[:1], bytesIo.read(1))
+        self.assertEqual(buf[1:5], bytesIo.read(4))
+        self.assertEqual(buf[5:], bytesIo.read(900))
+        self.assertEqual(self.EOF, bytesIo.read())
+
+    def testReadNoArgs(self):
+        buf = self.buftype("1234567890")
+        bytesIo = self.ioclass(buf)
+
+        self.assertEqual(buf, bytesIo.read())
+        self.assertEqual(self.EOF, bytesIo.read())
+
+    def testSeek(self):
+        buf = self.buftype("1234567890")
+        bytesIo = self.ioclass(buf)
+
+        bytesIo.read(5)
+        bytesIo.seek(0)
+        self.assertEqual(buf, bytesIo.read())
+
+        bytesIo.seek(3)
+        self.assertEqual(buf[3:], bytesIo.read())
+        self.assertRaises(TypeError, bytesIo.seek, 0.0)
+
+    def testTell(self):
+        buf = self.buftype("1234567890")
+        bytesIo = self.ioclass(buf)
+
+        self.assertEqual(0, bytesIo.tell())
+        bytesIo.seek(5)
+        self.assertEqual(5, bytesIo.tell())
+        bytesIo.seek(10000)
+        self.assertEqual(10000, bytesIo.tell())
+
+
+class MemoryTestMixin:
+
+    def test_detach(self):
+        buf = self.ioclass()
+        self.assertRaises(self.UnsupportedOperation, buf.detach)
+
+    def write_ops(self, f, t):
+        self.assertEqual(f.write(t("blah.")), 5)
+        self.assertEqual(f.seek(0), 0)
+        self.assertEqual(f.write(t("Hello.")), 6)
+        self.assertEqual(f.tell(), 6)
+        self.assertEqual(f.seek(5), 5)
+        self.assertEqual(f.tell(), 5)
+        self.assertEqual(f.write(t(" world\n\n\n")), 9)
+        self.assertEqual(f.seek(0), 0)
+        self.assertEqual(f.write(t("h")), 1)
+        self.assertEqual(f.truncate(12), 12)
+        self.assertEqual(f.tell(), 1)
+
+    def test_write(self):
+        buf = self.buftype("hello world\n")
+        memio = self.ioclass(buf)
+
+        self.write_ops(memio, self.buftype)
+        self.assertEqual(memio.getvalue(), buf)
+        memio = self.ioclass()
+        self.write_ops(memio, self.buftype)
+        self.assertEqual(memio.getvalue(), buf)
+        self.assertRaises(TypeError, memio.write, None)
+        memio.close()
+        self.assertRaises(ValueError, memio.write, self.buftype(""))
+
+    def test_writelines(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass()
+
+        self.assertEqual(memio.writelines([buf] * 100), None)
+        self.assertEqual(memio.getvalue(), buf * 100)
+        memio.writelines([])
+        self.assertEqual(memio.getvalue(), buf * 100)
+        memio = self.ioclass()
+        self.assertRaises(TypeError, memio.writelines, [buf] + [1])
+        self.assertEqual(memio.getvalue(), buf)
+        self.assertRaises(TypeError, memio.writelines, None)
+        memio.close()
+        self.assertRaises(ValueError, memio.writelines, [])
+
+    def test_writelines_error(self):
+        memio = self.ioclass()
+        def error_gen():
+            yield self.buftype('spam')
+            raise KeyboardInterrupt
+
+        self.assertRaises(KeyboardInterrupt, memio.writelines, error_gen())
+
+    def test_truncate(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertRaises(ValueError, memio.truncate, -1)
+        memio.seek(6)
+        self.assertEqual(memio.truncate(), 6)
+        self.assertEqual(memio.getvalue(), buf[:6])
+        self.assertEqual(memio.truncate(4), 4)
+        self.assertEqual(memio.getvalue(), buf[:4])
+        # truncate() accepts long objects
+        self.assertEqual(memio.truncate(4L), 4)
+        self.assertEqual(memio.getvalue(), buf[:4])
+        self.assertEqual(memio.tell(), 6)
+        memio.seek(0, 2)
+        memio.write(buf)
+        self.assertEqual(memio.getvalue(), buf[:4] + buf)
+        pos = memio.tell()
+        self.assertEqual(memio.truncate(None), pos)
+        self.assertEqual(memio.tell(), pos)
+        self.assertRaises(TypeError, memio.truncate, '0')
+        memio.close()
+        self.assertRaises(ValueError, memio.truncate, 0)
+
+    def test_init(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+        self.assertEqual(memio.getvalue(), buf)
+        memio = self.ioclass(None)
+        self.assertEqual(memio.getvalue(), self.EOF)
+        memio.__init__(buf * 2)
+        self.assertEqual(memio.getvalue(), buf * 2)
+        memio.__init__(buf)
+        self.assertEqual(memio.getvalue(), buf)
+
+    def test_read(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertEqual(memio.read(0), self.EOF)
+        self.assertEqual(memio.read(1), buf[:1])
+        # read() accepts long objects
+        self.assertEqual(memio.read(4L), buf[1:5])
+        self.assertEqual(memio.read(900), buf[5:])
+        self.assertEqual(memio.read(), self.EOF)
+        memio.seek(0)
+        self.assertEqual(memio.read(), buf)
+        self.assertEqual(memio.read(), self.EOF)
+        self.assertEqual(memio.tell(), 10)
+        memio.seek(0)
+        self.assertEqual(memio.read(-1), buf)
+        memio.seek(0)
+        self.assertEqual(type(memio.read()), type(buf))
+        memio.seek(100)
+        self.assertEqual(type(memio.read()), type(buf))
+        memio.seek(0)
+        self.assertEqual(memio.read(None), buf)
+        self.assertRaises(TypeError, memio.read, '')
+        memio.close()
+        self.assertRaises(ValueError, memio.read)
+
+    def test_readline(self):
+        buf = self.buftype("1234567890\n")
+        memio = self.ioclass(buf * 2)
+
+        self.assertEqual(memio.readline(0), self.EOF)
+        self.assertEqual(memio.readline(), buf)
+        self.assertEqual(memio.readline(), buf)
+        self.assertEqual(memio.readline(), self.EOF)
+        memio.seek(0)
+        self.assertEqual(memio.readline(5), buf[:5])
+        # readline() accepts long objects
+        self.assertEqual(memio.readline(5L), buf[5:10])
+        self.assertEqual(memio.readline(5), buf[10:15])
+        memio.seek(0)
+        self.assertEqual(memio.readline(-1), buf)
+        memio.seek(0)
+        self.assertEqual(memio.readline(0), self.EOF)
+
+        buf = self.buftype("1234567890\n")
+        memio = self.ioclass((buf * 3)[:-1])
+        self.assertEqual(memio.readline(), buf)
+        self.assertEqual(memio.readline(), buf)
+        self.assertEqual(memio.readline(), buf[:-1])
+        self.assertEqual(memio.readline(), self.EOF)
+        memio.seek(0)
+        self.assertEqual(type(memio.readline()), type(buf))
+        self.assertEqual(memio.readline(), buf)
+        self.assertRaises(TypeError, memio.readline, '')
+        memio.close()
+        self.assertRaises(ValueError,  memio.readline)
+
+    def test_readlines(self):
+        buf = self.buftype("1234567890\n")
+        memio = self.ioclass(buf * 10)
+
+        self.assertEqual(memio.readlines(), [buf] * 10)
+        memio.seek(5)
+        self.assertEqual(memio.readlines(), [buf[5:]] + [buf] * 9)
+        memio.seek(0)
+        # readlines() accepts long objects
+        self.assertEqual(memio.readlines(15L), [buf] * 2)
+        memio.seek(0)
+        self.assertEqual(memio.readlines(-1), [buf] * 10)
+        memio.seek(0)
+        self.assertEqual(memio.readlines(0), [buf] * 10)
+        memio.seek(0)
+        self.assertEqual(type(memio.readlines()[0]), type(buf))
+        memio.seek(0)
+        self.assertEqual(memio.readlines(None), [buf] * 10)
+        self.assertRaises(TypeError, memio.readlines, '')
+        memio.close()
+        self.assertRaises(ValueError, memio.readlines)
+
+    def test_iterator(self):
+        buf = self.buftype("1234567890\n")
+        memio = self.ioclass(buf * 10)
+
+        self.assertEqual(iter(memio), memio)
+        self.assertTrue(hasattr(memio, '__iter__'))
+        self.assertTrue(hasattr(memio, 'next'))
+        i = 0
+        for line in memio:
+            self.assertEqual(line, buf)
+            i += 1
+        self.assertEqual(i, 10)
+        memio.seek(0)
+        i = 0
+        for line in memio:
+            self.assertEqual(line, buf)
+            i += 1
+        self.assertEqual(i, 10)
+        memio = self.ioclass(buf * 2)
+        memio.close()
+        self.assertRaises(ValueError, next, memio)
+
+    def test_getvalue(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertEqual(memio.getvalue(), buf)
+        memio.read()
+        self.assertEqual(memio.getvalue(), buf)
+        self.assertEqual(type(memio.getvalue()), type(buf))
+        memio = self.ioclass(buf * 1000)
+        self.assertEqual(memio.getvalue()[-3:], self.buftype("890"))
+        memio = self.ioclass(buf)
+        memio.close()
+        self.assertRaises(ValueError, memio.getvalue)
+
+    def test_seek(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        memio.read(5)
+        self.assertRaises(ValueError, memio.seek, -1)
+        self.assertRaises(ValueError, memio.seek, 1, -1)
+        self.assertRaises(ValueError, memio.seek, 1, 3)
+        self.assertEqual(memio.seek(0), 0)
+        self.assertEqual(memio.seek(0, 0), 0)
+        self.assertEqual(memio.read(), buf)
+        self.assertEqual(memio.seek(3), 3)
+        # seek() accepts long objects
+        self.assertEqual(memio.seek(3L), 3)
+        self.assertEqual(memio.seek(0, 1), 3)
+        self.assertEqual(memio.read(), buf[3:])
+        self.assertEqual(memio.seek(len(buf)), len(buf))
+        self.assertEqual(memio.read(), self.EOF)
+        memio.seek(len(buf) + 1)
+        self.assertEqual(memio.read(), self.EOF)
+        self.assertEqual(memio.seek(0, 2), len(buf))
+        self.assertEqual(memio.read(), self.EOF)
+        memio.close()
+        self.assertRaises(ValueError, memio.seek, 0)
+
+    def test_overseek(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertEqual(memio.seek(len(buf) + 1), 11)
+        self.assertEqual(memio.read(), self.EOF)
+        self.assertEqual(memio.tell(), 11)
+        self.assertEqual(memio.getvalue(), buf)
+        memio.write(self.EOF)
+        self.assertEqual(memio.getvalue(), buf)
+        memio.write(buf)
+        self.assertEqual(memio.getvalue(), buf + self.buftype('\0') + buf)
+
+    def test_tell(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertEqual(memio.tell(), 0)
+        memio.seek(5)
+        self.assertEqual(memio.tell(), 5)
+        memio.seek(10000)
+        self.assertEqual(memio.tell(), 10000)
+        memio.close()
+        self.assertRaises(ValueError, memio.tell)
+
+    def test_flush(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertEqual(memio.flush(), None)
+
+    def test_flags(self):
+        memio = self.ioclass()
+
+        self.assertEqual(memio.writable(), True)
+        self.assertEqual(memio.readable(), True)
+        self.assertEqual(memio.seekable(), True)
+        self.assertEqual(memio.isatty(), False)
+        self.assertEqual(memio.closed, False)
+        memio.close()
+        self.assertRaises(ValueError, memio.writable)
+        self.assertRaises(ValueError, memio.readable)
+        self.assertRaises(ValueError, memio.seekable)
+        self.assertRaises(ValueError, memio.isatty)
+        self.assertEqual(memio.closed, True)
+
+    def test_subclassing(self):
+        buf = self.buftype("1234567890")
+        def test1():
+            class MemIO(self.ioclass):
+                pass
+            m = MemIO(buf)
+            return m.getvalue()
+        def test2():
+            class MemIO(self.ioclass):
+                def __init__(me, a, b):
+                    self.ioclass.__init__(me, a)
+            m = MemIO(buf, None)
+            return m.getvalue()
+        self.assertEqual(test1(), buf)
+        self.assertEqual(test2(), buf)
+
+    def test_instance_dict_leak(self):
+        # Test case for issue #6242.
+        # This will be caught by regrtest.py -R if this leak.
+        for _ in range(100):
+            memio = self.ioclass()
+            memio.foo = 1
+
+    def test_pickling(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+        memio.foo = 42
+        memio.seek(2)
+
+        class PickleTestMemIO(self.ioclass):
+            def __init__(me, initvalue, foo):
+                self.ioclass.__init__(me, initvalue)
+                me.foo = foo
+            # __getnewargs__ is undefined on purpose. This checks that PEP 307
+            # is used to provide pickling support.
+
+        # Pickle expects the class to be on the module level. Here we use a
+        # little hack to allow the PickleTestMemIO class to derive from
+        # self.ioclass without having to define all combinations explicitly on
+        # the module-level.
+        import __main__
+        PickleTestMemIO.__module__ = '__main__'
+        __main__.PickleTestMemIO = PickleTestMemIO
+        submemio = PickleTestMemIO(buf, 80)
+        submemio.seek(2)
+
+        # We only support pickle protocol 2 and onward since we use extended
+        # __reduce__ API of PEP 307 to provide pickling support.
+        for proto in range(2, pickle.HIGHEST_PROTOCOL):
+            for obj in (memio, submemio):
+                obj2 = pickle.loads(pickle.dumps(obj, protocol=proto))
+                self.assertEqual(obj.getvalue(), obj2.getvalue())
+                self.assertEqual(obj.__class__, obj2.__class__)
+                self.assertEqual(obj.foo, obj2.foo)
+                self.assertEqual(obj.tell(), obj2.tell())
+                obj.close()
+                self.assertRaises(ValueError, pickle.dumps, obj, proto)
+        del __main__.PickleTestMemIO
+
+
+class PyBytesIOTest(MemoryTestMixin, MemorySeekTestMixin, unittest.TestCase):
+
+    UnsupportedOperation = pyio.UnsupportedOperation
+
+    # When Jython tries to use UnsupportedOperation as _pyio defines it, it runs
+    # into a problem with multiple inheritance and the slots array: issue 1996.
+    # Override the affected test version just so we can skip it visibly.
+    @unittest.skipIf(support.is_jython, "FIXME: Jython issue 1996")
+    def test_detach(self):
+        pass
+
+    @staticmethod
+    def buftype(s):
+        return s.encode("ascii")
+    ioclass = pyio.BytesIO
+    EOF = b""
+
+    def test_read1(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertRaises(TypeError, memio.read1)
+        self.assertEqual(memio.read(), buf)
+
+    def test_readinto(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        b = bytearray(b"hello")
+        self.assertEqual(memio.readinto(b), 5)
+        self.assertEqual(b, b"12345")
+        self.assertEqual(memio.readinto(b), 5)
+        self.assertEqual(b, b"67890")
+        self.assertEqual(memio.readinto(b), 0)
+        self.assertEqual(b, b"67890")
+        b = bytearray(b"hello world")
+        memio.seek(0)
+        self.assertEqual(memio.readinto(b), 10)
+        self.assertEqual(b, b"1234567890d")
+        b = bytearray(b"")
+        memio.seek(0)
+        self.assertEqual(memio.readinto(b), 0)
+        self.assertEqual(b, b"")
+        self.assertRaises(TypeError, memio.readinto, '')
+        import array
+        a = array.array(b'b', b"hello world")
+        memio = self.ioclass(buf)
+        memio.readinto(a)
+        self.assertEqual(a.tostring(), b"1234567890d")
+        memio.close()
+        self.assertRaises(ValueError, memio.readinto, b)
+        memio = self.ioclass(b"123")
+        b = bytearray()
+        memio.seek(42)
+        memio.readinto(b)
+        self.assertEqual(b, b"")
+
+    def test_relative_seek(self):
+        buf = self.buftype("1234567890")
+        memio = self.ioclass(buf)
+
+        self.assertEqual(memio.seek(-1, 1), 0)
+        self.assertEqual(memio.seek(3, 1), 3)
+        self.assertEqual(memio.seek(-4, 1), 0)
+        self.assertEqual(memio.seek(-1, 2), 9)
+        self.assertEqual(memio.seek(1, 1), 10)
+        self.assertEqual(memio.seek(1, 2), 11)
+        memio.seek(-3, 2)
+        self.assertEqual(memio.read(), buf[-3:])
+        memio.seek(0)
+        memio.seek(1, 1)
+        self.assertEqual(memio.read(), buf[1:])
+
+    def test_unicode(self):
+        memio = self.ioclass()
+
+        self.assertRaises(TypeError, self.ioclass, "1234567890")
+        self.assertRaises(TypeError, memio.write, "1234567890")
+        self.assertRaises(TypeError, memio.writelines, ["1234567890"])
+
+    def test_bytes_array(self):
+        buf = b"1234567890"
+        import array
+        a = array.array(b'b', buf)
+        memio = self.ioclass(a)
+        self.assertEqual(memio.getvalue(), buf)
+        self.assertEqual(memio.write(a), 10)
+        self.assertEqual(memio.getvalue(), buf)
+
+    def test_issue5449(self):
+        buf = self.buftype("1234567890")
+        self.ioclass(initial_bytes=buf)
+        self.assertRaises(TypeError, self.ioclass, buf, foo=None)
+
+
+class TextIOTestMixin:
+
+    def test_newlines_property(self):
+        memio = self.ioclass(newline=None)
+        # The C StringIO decodes newlines in write() calls, but the Python
+        # implementation only does when reading.  This function forces them to
+        # be decoded for testing.
+        def force_decode():
+            memio.seek(0)
+            memio.read()
+        self.assertEqual(memio.newlines, None)
+        memio.write("a\n")
+        force_decode()
+        self.assertEqual(memio.newlines, "\n")
+        memio.write("b\r\n")
+        force_decode()
+        self.assertEqual(memio.newlines, ("\n", "\r\n"))
+        memio.write("c\rd")
+        force_decode()
+        self.assertEqual(memio.newlines, ("\r", "\n", "\r\n"))
+
+    def test_relative_seek(self):
+        memio = self.ioclass()
+
+        self.assertRaises(IOError, memio.seek, -1, 1)
+        self.assertRaises(IOError, memio.seek, 3, 1)
+        self.assertRaises(IOError, memio.seek, -3, 1)
+        self.assertRaises(IOError, memio.seek, -1, 2)
+        self.assertRaises(IOError, memio.seek, 1, 1)
+        self.assertRaises(IOError, memio.seek, 1, 2)
+
+    def test_textio_properties(self):
+        memio = self.ioclass()
+
+        # These are just dummy values but we nevertheless check them for fear
+        # of unexpected breakage.
+        self.assertIsNone(memio.encoding)
+        self.assertIsNone(memio.errors)
+        self.assertFalse(memio.line_buffering)
+
+    def test_newline_none(self):
+        # newline=None
+        memio = self.ioclass("a\nb\r\nc\rd", newline=None)
+        self.assertEqual(list(memio), ["a\n", "b\n", "c\n", "d"])
+        memio.seek(0)
+        self.assertEqual(memio.read(1), "a")
+        self.assertEqual(memio.read(2), "\nb")
+        self.assertEqual(memio.read(2), "\nc")
+        self.assertEqual(memio.read(1), "\n")
+        memio = self.ioclass(newline=None)
+        self.assertEqual(2, memio.write("a\n"))
+        self.assertEqual(3, memio.write("b\r\n"))
+        self.assertEqual(3, memio.write("c\rd"))
+        memio.seek(0)
+        self.assertEqual(memio.read(), "a\nb\nc\nd")
+        memio = self.ioclass("a\r\nb", newline=None)
+        self.assertEqual(memio.read(3), "a\nb")
+
+    def test_newline_empty(self):
+        # newline=""
+        memio = self.ioclass("a\nb\r\nc\rd", newline="")
+        self.assertEqual(list(memio), ["a\n", "b\r\n", "c\r", "d"])
+        memio.seek(0)
+        self.assertEqual(memio.read(4), "a\nb\r")
+        self.assertEqual(memio.read(2), "\nc")
+        self.assertEqual(memio.read(1), "\r")
+        memio = self.ioclass(newline="")
+        self.assertEqual(2, memio.write("a\n"))
+        self.assertEqual(2, memio.write("b\r"))
+        self.assertEqual(2, memio.write("\nc"))
+        self.assertEqual(2, memio.write("\rd"))
+        memio.seek(0)
+        self.assertEqual(list(memio), ["a\n", "b\r\n", "c\r", "d"])
+
+    def test_newline_lf(self):
+        # newline="\n"
+        memio = self.ioclass("a\nb\r\nc\rd")
+        self.assertEqual(list(memio), ["a\n", "b\r\n", "c\rd"])
+
+    def test_newline_cr(self):
+        # newline="\r"
+        memio = self.ioclass("a\nb\r\nc\rd", newline="\r")
+        self.assertEqual(memio.read(), "a\rb\r\rc\rd")
+        memio.seek(0)
+        self.assertEqual(list(memio), ["a\r", "b\r", "\r", "c\r", "d"])
+
+    def test_newline_crlf(self):
+        # newline="\r\n"
+        memio = self.ioclass("a\nb\r\nc\rd", newline="\r\n")
+        self.assertEqual(memio.read(), "a\r\nb\r\r\nc\rd")
+        memio.seek(0)
+        self.assertEqual(list(memio), ["a\r\n", "b\r\r\n", "c\rd"])
+
+    def test_issue5265(self):
+        # StringIO can duplicate newlines in universal newlines mode
+        memio = self.ioclass("a\r\nb\r\n", newline=None)
+        self.assertEqual(memio.read(5), "a\nb\n")
+
+
+class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin,
+                     TextIOTestMixin, unittest.TestCase):
+    buftype = unicode
+    ioclass = pyio.StringIO
+    UnsupportedOperation = pyio.UnsupportedOperation
+    EOF = ""
+
+    # When Jython tries to use UnsupportedOperation as _pyio defines it, it runs
+    # into a problem with multiple inheritance and the slots array: issue 1996.
+    # Override the affected test version just so we can skip it visibly.
+    @unittest.skipIf(support.is_jython, "FIXME: Jython issue 1996")
+    def test_detach(self):
+        pass
+
+
+class PyStringIOPickleTest(TextIOTestMixin, unittest.TestCase):
+    """Test if pickle restores properly the internal state of StringIO.
+    """
+    buftype = unicode
+    UnsupportedOperation = pyio.UnsupportedOperation
+    EOF = ""
+
+    class ioclass(pyio.StringIO):
+        def __new__(cls, *args, **kwargs):
+            return pickle.loads(pickle.dumps(pyio.StringIO(*args, **kwargs)))
+        def __init__(self, *args, **kwargs):
+            pass
+
+
+class CBytesIOTest(PyBytesIOTest):
+    ioclass = io.BytesIO
+    UnsupportedOperation = io.UnsupportedOperation
+
+    test_bytes_array = unittest.skip(
+        "array.array() does not have the new buffer API"
+    )(PyBytesIOTest.test_bytes_array)
+
+    # Re-instate test_detach skipped by Jython in PyBytesIOTest
+    if support.is_jython: # FIXME: Jython issue 1996
+        test_detach = MemoryTestMixin.test_detach
+
+    def test_getstate(self):
+        memio = self.ioclass()
+        state = memio.__getstate__()
+        self.assertEqual(len(state), 3)
+        bytearray(state[0]) # Check if state[0] supports the buffer interface.
+        self.assertIsInstance(state[1], int)
+        self.assertTrue(isinstance(state[2], dict) or state[2] is None)
+        memio.close()
+        self.assertRaises(ValueError, memio.__getstate__)
+
+    def test_setstate(self):
+        # This checks whether __setstate__ does proper input validation.
+        memio = self.ioclass()
+        memio.__setstate__((b"no error", 0, None))
+        memio.__setstate__((bytearray(b"no error"), 0, None))
+        memio.__setstate__((b"no error", 0, {'spam': 3}))
+        self.assertRaises(ValueError, memio.__setstate__, (b"", -1, None))
+        self.assertRaises(TypeError, memio.__setstate__, ("unicode", 0, None))
+        self.assertRaises(TypeError, memio.__setstate__, (b"", 0.0, None))
+        self.assertRaises(TypeError, memio.__setstate__, (b"", 0, 0))
+        self.assertRaises(TypeError, memio.__setstate__, (b"len-test", 0))
+        self.assertRaises(TypeError, memio.__setstate__)
+        self.assertRaises(TypeError, memio.__setstate__, 0)
+        memio.close()
+        self.assertRaises(ValueError, memio.__setstate__, (b"closed", 0, None))
+
+    check_sizeof = support.check_sizeof
+
+    @support.cpython_only
+    def test_sizeof(self):
+        basesize = support.calcobjsize(b'P2PP2P')
+        check = self.check_sizeof
+        self.assertEqual(object.__sizeof__(io.BytesIO()), basesize)
+        check(io.BytesIO(), basesize )
+        check(io.BytesIO(b'a'), basesize + 1 + 1 )
+        check(io.BytesIO(b'a' * 1000), basesize + 1000 + 1 )
+
+class CStringIOTest(PyStringIOTest):
+    ioclass = io.StringIO
+    UnsupportedOperation = io.UnsupportedOperation
+
+    # XXX: For the Python version of io.StringIO, this is highly
+    # dependent on the encoding used for the underlying buffer.
+
+    # Re-instate test_detach skipped by Jython in PyBytesIOTest
+    if support.is_jython: # FIXME: Jython issue 1996
+        test_detach = MemoryTestMixin.test_detach
+
+    # This test checks that tell() results are consistent with the length of
+    # text written, but this is not documented in the API: only that seek()
+    # accept what tell() returns.
+    @unittest.skipIf(support.is_jython, "Exact value of tell() is CPython specific")
+    def test_widechar(self):
+        buf = self.buftype("\U0002030a\U00020347")
+        memio = self.ioclass(buf)
+
+        self.assertEqual(memio.getvalue(), buf)
+        self.assertEqual(memio.write(buf), len(buf))
+        self.assertEqual(memio.tell(), len(buf))
+        self.assertEqual(memio.getvalue(), buf)
+        self.assertEqual(memio.write(buf), len(buf))
+        self.assertEqual(memio.tell(), len(buf) * 2)
+        self.assertEqual(memio.getvalue(), buf + buf)
+
+    # This test checks that seek() accepts what tell() returns, without requiring
+    # that tell() return a particular absolute value. Conceived for Jython, but
+    # probably universal.
+    def test_widechar_seek(self):
+        buf = self.buftype("\U0002030aX\u00ca\U00020347\u05d1Y\u0628Z")
+        memio = self.ioclass(buf)
+        self.assertEqual(memio.getvalue(), buf)
+
+        # For each character in buf, read it back from memio and its tell value
+        chars = list(buf)
+        tells = list()
+        for ch in chars :
+            tells.append(memio.tell())
+            self.assertEqual(memio.read(1), ch)
+
+        # For each character in buf, seek to it and check it's there
+        chpos = zip(chars, tells)
+        chpos.reverse()
+        for ch, pos in chpos:
+            memio.seek(pos)
+            self.assertEqual(memio.read(1), ch)
+
+        # Check write after seek to end
+        memio.seek(0, 2)
+        self.assertEqual(memio.write(buf), len(buf))
+        self.assertEqual(memio.getvalue(), buf + buf)
+
+    def test_getstate(self):
+        memio = self.ioclass()
+        state = memio.__getstate__()
+        self.assertEqual(len(state), 4)
+        self.assertIsInstance(state[0], unicode)
+        self.assertIsInstance(state[1], str)
+        self.assertIsInstance(state[2], int)
+        self.assertTrue(isinstance(state[3], dict) or state[3] is None)
+        memio.close()
+        self.assertRaises(ValueError, memio.__getstate__)
+
+    def test_setstate(self):
+        # This checks whether __setstate__ does proper input validation.
+        memio = self.ioclass()
+        memio.__setstate__(("no error", "\n", 0, None))
+        memio.__setstate__(("no error", "", 0, {'spam': 3}))
+        self.assertRaises(ValueError, memio.__setstate__, ("", "f", 0, None))
+        self.assertRaises(ValueError, memio.__setstate__, ("", "", -1, None))
+        self.assertRaises(TypeError, memio.__setstate__, (b"", "", 0, None))
+        # trunk is more tolerant than py3k on the type of the newline param
+        #self.assertRaises(TypeError, memio.__setstate__, ("", b"", 0, None))
+        self.assertRaises(TypeError, memio.__setstate__, ("", "", 0.0, None))
+        self.assertRaises(TypeError, memio.__setstate__, ("", "", 0, 0))
+        self.assertRaises(TypeError, memio.__setstate__, ("len-test", 0))
+        self.assertRaises(TypeError, memio.__setstate__)
+        self.assertRaises(TypeError, memio.__setstate__, 0)
+        memio.close()
+        self.assertRaises(ValueError, memio.__setstate__, ("closed", "", 0, None))
+
+
+class CStringIOPickleTest(PyStringIOPickleTest):
+    UnsupportedOperation = io.UnsupportedOperation
+
+    class ioclass(io.StringIO):
+        def __new__(cls, *args, **kwargs):
+            return pickle.loads(pickle.dumps(io.StringIO(*args, **kwargs),
+                                             protocol=2))
+        def __init__(self, *args, **kwargs):
+            pass
+
+
+def test_main():
+    tests = [PyBytesIOTest, PyStringIOTest, CBytesIOTest, CStringIOTest,
+             PyStringIOPickleTest, CStringIOPickleTest]
+    support.run_unittest(*tests)
+
+if __name__ == '__main__':
+    test_main()
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -224,6 +224,8 @@
         res = platform.dist()
 
     def test_libc_ver(self):
+        if sys.executable is None:
+            return
         import os
         if os.path.isdir(sys.executable) and \
            os.path.exists(sys.executable+'.exe'):
diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py
--- a/Lib/test/test_site.py
+++ b/Lib/test/test_site.py
@@ -196,7 +196,6 @@
             env=env)
         self.assertEqual(rc, 1)
 
-    @unittest.skipIf(is_jython, "FIXME: not on Jython yet.")
     def test_getuserbase(self):
         site.USER_BASE = None
         user_base = site.getuserbase()
diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py
--- a/Lib/test/test_support.py
+++ b/Lib/test/test_support.py
@@ -18,6 +18,14 @@
 import UserDict
 import re
 import time
+import struct
+import sysconfig
+
+try:
+    import _testcapi
+except ImportError:
+    _testcapi = None
+
 try:
     import thread
 except ImportError:
@@ -181,15 +189,79 @@
     except KeyError:
         pass
 
+if sys.platform.startswith("win"):
+    def _waitfor(func, pathname, waitall=False):
+        # Peform the operation
+        func(pathname)
+        # Now setup the wait loop
+        if waitall:
+            dirname = pathname
+        else:
+            dirname, name = os.path.split(pathname)
+            dirname = dirname or '.'
+        # Check for `pathname` to be removed from the filesystem.
+        # The exponential backoff of the timeout amounts to a total
+        # of ~1 second after which the deletion is probably an error
+        # anyway.
+        # Testing on a i7 at 4.3GHz shows that usually only 1 iteration is
+        # required when contention occurs.
+        timeout = 0.001
+        while timeout < 1.0:
+            # Note we are only testing for the existance of the file(s) in
+            # the contents of the directory regardless of any security or
+            # access rights.  If we have made it this far, we have sufficient
+            # permissions to do that much using Python's equivalent of the
+            # Windows API FindFirstFile.
+            # Other Windows APIs can fail or give incorrect results when
+            # dealing with files that are pending deletion.
+            L = os.listdir(dirname)
+            if not (L if waitall else name in L):
+                return
+            # Increase the timeout and try again
+            time.sleep(timeout)
+            timeout *= 2
+        warnings.warn('tests may fail, delete still pending for ' + pathname,
+                      RuntimeWarning, stacklevel=4)
+
+    def _unlink(filename):
+        _waitfor(os.unlink, filename)
+
+    def _rmdir(dirname):
+        _waitfor(os.rmdir, dirname)
+
+    def _rmtree(path):
+        def _rmtree_inner(path):
+            for name in os.listdir(path):
+                fullname = os.path.join(path, name)
+                if os.path.isdir(fullname):
+                    _waitfor(_rmtree_inner, fullname, waitall=True)
+                    os.rmdir(fullname)
+                else:
+                    os.unlink(fullname)
+        _waitfor(_rmtree_inner, path, waitall=True)
+        _waitfor(os.rmdir, path)
+else:
+    _unlink = os.unlink
+    _rmdir = os.rmdir
+    _rmtree = shutil.rmtree
+
 def unlink(filename):
     try:
-        os.unlink(filename)
+        _unlink(filename)
     except OSError:
         pass
 
+def rmdir(dirname):
+    try:
+        _rmdir(dirname)
+    except OSError as error:
+        # The directory need not exist.
+        if error.errno != errno.ENOENT:
+            raise
+
 def rmtree(path):
     try:
-        shutil.rmtree(path)
+        _rmtree(path)
     except OSError, e:
         # Unix returns ENOENT, Windows returns ESRCH.
         if e.errno not in (errno.ENOENT, errno.ESRCH):
@@ -465,7 +537,7 @@
     the CWD, an error is raised.  If it's True, only a warning is raised
     and the original CWD is used.
     """
-    if isinstance(name, unicode):
+    if have_unicode and isinstance(name, unicode):
         try:
             name = name.encode(sys.getfilesystemencoding() or 'ascii')
         except UnicodeEncodeError:
@@ -813,6 +885,9 @@
         ('EAI_FAIL', -4),
         ('EAI_NONAME', -2),
         ('EAI_NODATA', -5),
+        # Windows defines EAI_NODATA as 11001 but idiotic getaddrinfo()
+        # implementation actually returns WSANO_DATA i.e. 11004.
+        ('WSANO_DATA', 11004),
     ]
 
     denied = ResourceDenied("Resource '%s' is not available" % resource_name)
@@ -886,6 +961,33 @@
     return captured_output("stdin")
 
 
+_header = '2P'
+if hasattr(sys, "gettotalrefcount"):
+    _header = '2P' + _header
+_vheader = _header + 'P'
+
+def calcobjsize(fmt):
+    return struct.calcsize(_header + fmt + '0P')
+
+def calcvobjsize(fmt):
+    return struct.calcsize(_vheader + fmt + '0P')
+
+
+_TPFLAGS_HAVE_GC = 1<<14
+_TPFLAGS_HEAPTYPE = 1<<9
+
+def check_sizeof(test, o, size):
+    result = sys.getsizeof(o)
+    # add GC header size
+    if (_testcapi and\
+        (type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\
+        ((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))):
+        size += _testcapi.SIZEOF_PYGC_HEAD
+    msg = 'wrong size for %s: got %d, expected %d' \
+            % (type(o), result, size)
+    test.assertEqual(result, size, msg)
+
+
 #=======================================================================
 # Decorator for running a function in a different locale, correctly resetting
 # it afterwards.
@@ -994,7 +1096,7 @@
         return wrapper
     return decorator
 
-def precisionbigmemtest(size, memuse, overhead=5*_1M):
+def precisionbigmemtest(size, memuse, overhead=5*_1M, dry_run=True):
     def decorator(f):
         def wrapper(self):
             if not real_max_memuse:
@@ -1002,11 +1104,12 @@
             else:
                 maxsize = size
 
-                if real_max_memuse and real_max_memuse < maxsize * memuse:
-                    if verbose:
-                        sys.stderr.write("Skipping %s because of memory "
-                                         "constraint\n" % (f.__name__,))
-                    return
+            if ((real_max_memuse or not dry_run)
+                and real_max_memuse < maxsize * memuse):
+                if verbose:
+                    sys.stderr.write("Skipping %s because of memory "
+                                     "constraint\n" % (f.__name__,))
+                return
 
             return f(self, maxsize)
         wrapper.size = size
@@ -1144,6 +1247,16 @@
             suite.addTest(unittest.makeSuite(cls))
     _run_suite(suite)
 
+#=======================================================================
+# Check for the presence of docstrings.
+
+HAVE_DOCSTRINGS = (check_impl_detail(cpython=False) or
+                   sys.platform == 'win32' or
+                   sysconfig.get_config_var('WITH_DOC_STRINGS'))
+
+requires_docstrings = unittest.skipUnless(HAVE_DOCSTRINGS,
+                                          "test requires docstrings")
+
 
 #=======================================================================
 # doctest driver.
@@ -1231,6 +1344,33 @@
             except:
                 break
 
+ at contextlib.contextmanager
+def swap_attr(obj, attr, new_val):
+    """Temporary swap out an attribute with a new object.
+
+    Usage:
+        with swap_attr(obj, "attr", 5):
+            ...
+
+        This will set obj.attr to 5 for the duration of the with: block,
+        restoring the old value at the end of the block. If `attr` doesn't
+        exist on `obj`, it will be created and then deleted at the end of the
+        block.
+    """
+    if hasattr(obj, attr):
+        real_val = getattr(obj, attr)
+        setattr(obj, attr, new_val)
+        try:
+            yield
+        finally:
+            setattr(obj, attr, real_val)
+    else:
+        setattr(obj, attr, new_val)
+        try:
+            yield
+        finally:
+            delattr(obj, attr)
+
 def py3k_bytes(b):
     """Emulate the py3k bytes() constructor.
 
diff --git a/Lib/test/test_threading_jy.py b/Lib/test/test_threading_jy.py
--- a/Lib/test/test_threading_jy.py
+++ b/Lib/test/test_threading_jy.py
@@ -43,6 +43,15 @@
     def _sleep(self, n):
         time.sleep(random.random())
 
+    def test_issue1988(self):
+        cond = threading.Condition(threading.Lock())
+        locked = False
+        try:
+            locked = cond.acquire(False)
+        finally:
+            if locked:
+                cond.release()
+
 
 class TwistedTestCase(unittest.TestCase):
     
diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py
--- a/Lib/test/test_urllib2.py
+++ b/Lib/test/test_urllib2.py
@@ -663,6 +663,11 @@
             self.assertEqual(headers.get("Content-type"), mimetype)
             self.assertEqual(int(headers["Content-length"]), len(data))
 
+    # This test isn't working on Ubuntu on an Apple Intel powerbook,
+    # Jython 2.7b1+ (default:6b4a1088566e, Feb 10 2013, 14:36:47) 
+    # [OpenJDK 64-Bit Server VM (Oracle Corporation)] on java1.7.0_09
+    @unittest.skipIf(test_support.is_jython,
+                     "FIXME: Currently not working on jython")
     def test_file(self):
         import rfc822, socket
         h = urllib2.FileHandler()
@@ -1112,7 +1117,6 @@
     def test_basic_auth_with_single_quoted_realm(self):
         self.test_basic_auth(quote_char="'")
 
-    @unittest.skipIf(test_support.is_jython, "Currently not working on jython")
     def test_basic_auth_with_unquoted_realm(self):
         opener = OpenerDirector()
         password_manager = MockPasswordManager()
diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py
--- a/Lib/test/test_warnings.py
+++ b/Lib/test/test_warnings.py
@@ -189,6 +189,7 @@
             self.assertEqual(str(w[-1].message), text)
             self.assertTrue(w[-1].category is UserWarning)
 
+ at unittest.skipIf(test_support.is_jython, "No _warnings impl yet")
 class CFilterTests(BaseTest, FilterTests):
     module = c_warnings
 
@@ -351,6 +352,7 @@
             self.module.warn(BadStrWarning())
 
 
+ at unittest.skipIf(test_support.is_jython, "No _warnings impl yet")
 class CWarnTests(BaseTest, WarnTests):
     module = c_warnings
 
@@ -403,6 +405,7 @@
         self.assertFalse(out.strip())
         self.assertNotIn(b'RuntimeWarning', err)
 
+ at unittest.skipIf(test_support.is_jython, "No _warnings impl yet")
 class CWCmdLineTests(BaseTest, WCmdLineTests):
     module = c_warnings
 
@@ -410,6 +413,7 @@
     module = py_warnings
 
 
+ at unittest.skipIf(test_support.is_jython, "No _warnings impl yet")
 class _WarningsTests(BaseTest):
 
     """Tests specific to the _warnings module."""
@@ -593,6 +597,7 @@
                                 file_object, expected_file_line)
         self.assertEqual(expect, file_object.getvalue())
 
+ at unittest.skipIf(test_support.is_jython, "No _warnings impl yet")
 class CWarningsDisplayTests(BaseTest, WarningsDisplayTests):
     module = c_warnings
 
@@ -703,6 +708,7 @@
                 wmod.warn("foo")
 
 
+ at unittest.skipIf(test_support.is_jython, "No _warnings impl yet")
 class CCatchWarningTests(CatchWarningTests):
     module = c_warnings
 
@@ -742,6 +748,7 @@
                 "['ignore::UnicodeWarning', 'ignore::DeprecationWarning']")
         self.assertEqual(p.wait(), 0)
 
+ at unittest.skipIf(test_support.is_jython, "No _warnings impl yet")
 class CEnvironmentVariableTests(EnvironmentVariableTests):
     module = c_warnings
 
@@ -751,7 +758,9 @@
 
 def test_main():
     py_warnings.onceregistry.clear()
-    c_warnings.onceregistry.clear()
+    # No _warnings in _jython yet.
+    if not test_support.is_jython:
+        c_warnings.onceregistry.clear()
     test_support.run_unittest(
         CFilterTests,
         PyFilterTests,
diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py
--- a/Lib/test/test_zlib.py
+++ b/Lib/test/test_zlib.py
@@ -24,15 +24,21 @@
         self.assertEqual(zlib.crc32("", 1), 1)
         self.assertEqual(zlib.crc32("", 432), 432)
 
-    @unittest.skipIf(is_jython, "FIXME #1859: not working on Jython")
+    def test_adler32(self):
+        self.assertEqual(zlib.adler32(""), zlib.adler32("", 1))
+
+    @unittest.skipIf(is_jython, "jython uses java.util.zip.Adler32, \
+                which does not support a start value other than 1")
     def test_adler32start(self):
-        self.assertEqual(zlib.adler32(""), zlib.adler32("", 1))
         self.assertTrue(zlib.adler32("abc", 0xffffffff))
 
-    @unittest.skipIf(is_jython, "FIXME #1859: not working on Jython")
     def test_adler32empty(self):
+        self.assertEqual(zlib.adler32("", 1), 1)
+
+    @unittest.skipIf(is_jython, "jython uses java.util.zip.Adler32, \
+                which does not support a start value other than 1")
+    def test_adler32empty_start(self):
         self.assertEqual(zlib.adler32("", 0), 0)
-        self.assertEqual(zlib.adler32("", 1), 1)
         self.assertEqual(zlib.adler32("", 432), 432)
 
     def assertEqual32(self, seen, expected):
@@ -40,16 +46,19 @@
         # This is important if bit 31 (0x08000000L) is set.
         self.assertEqual(seen & 0x0FFFFFFFFL, expected & 0x0FFFFFFFFL)
 
-    @unittest.skipIf(is_jython, "FIXME #1859: not working on Jython")
     def test_penguins(self):
         self.assertEqual32(zlib.crc32("penguin", 0), 0x0e5c1a120L)
         self.assertEqual32(zlib.crc32("penguin", 1), 0x43b6aa94)
-        self.assertEqual32(zlib.adler32("penguin", 0), 0x0bcf02f6)
         self.assertEqual32(zlib.adler32("penguin", 1), 0x0bd602f7)
 
         self.assertEqual(zlib.crc32("penguin"), zlib.crc32("penguin", 0))
         self.assertEqual(zlib.adler32("penguin"),zlib.adler32("penguin",1))
 
+    @unittest.skipIf(is_jython, "jython uses java.util.zip.Adler32, \
+                which does not support a start value other than 1")
+    def test_penguins_start(self):
+        self.assertEqual32(zlib.adler32("penguin", 0), 0x0bcf02f6)
+
     def test_abcdefghijklmnop(self):
         """test issue1202 compliance: signed crc32, adler32 in 2.x"""
         foo = 'abcdefghijklmnop'
@@ -101,23 +110,41 @@
 
 
 class BaseCompressTestCase(object):
-    @unittest.skipIf(is_jython, "FIXME #1859: appears to hang on Jython")
+
     def check_big_compress_buffer(self, size, compress_func):
         _1M = 1024 * 1024
-        fmt = "%%0%dx" % (2 * _1M)
-        # Generate 10MB worth of random, and expand it by repeating it.
-        # The assumption is that zlib's memory is not big enough to exploit
-        # such spread out redundancy.
-        data = ''.join([binascii.a2b_hex(fmt % random.getrandbits(8 * _1M))
-                        for i in range(10)])
-        data = data * (size // len(data) + 1)
+        if not is_jython:
+            # Generate 10MB worth of random, and expand it by repeating it.
+            # The assumption is that zlib's memory is not big enough to exploit
+            # such spread out redundancy.
+            fmt = "%%0%dx" % (2 * _1M)
+            data = ''.join([binascii.a2b_hex(fmt % random.getrandbits(8 * _1M))
+                            for i in range(10)])
+            data = data * (size // len(data) + 1)
+        else:
+            #
+            # The original version of this test passes fine on cpython,
+            # but appears to hang on jython, because of the time taken to
+            # format a very large integer as a hexadecimal string.
+            # See this issue for details
+            # http://bugs.jython.org/issue2013
+            # Since testing string formatting is not the purpose of the test
+            # it is necessary to generate the random test data in a different
+            # way on jython. (There may be a better way than what I have 
+            # implemented here)
+            #
+            from java.math import BigInteger
+            from java.util import Random
+            num_bits = 8 * _1M # causes "java.lang.OutOfMemoryError: Java heap space"
+            num_bits = _1M
+            data = ''.join([str(BigInteger((num_bits), Random()).toByteArray())
+                            for i in range(10)])
         try:
             compress_func(data)
         finally:
             # Release memory
             data = None
 
-    @unittest.skipIf(is_jython, "FIXME #1859: appears to hang on Jython")
     def check_big_decompress_buffer(self, size, decompress_func):
         data = 'x' * size
         try:
@@ -146,7 +173,8 @@
         x = zlib.compress(data)
         self.assertEqual(zlib.decompress(x), data)
 
-    @unittest.skipIf(is_jython, "FIXME #1859: not working on Jython")
+    @unittest.skipIf(is_jython, "jython uses java.util.zip.Inflater, \
+                which accepts incomplete streams without error")
     def test_incomplete_stream(self):
         # An useful error message is given
         x = zlib.compress(HAMLET_SCENE)
@@ -163,6 +191,16 @@
 
     @precisionbigmemtest(size=_1G + 1024 * 1024, memuse=2)
     def test_big_decompress_buffer(self, size):
+        """
+        This is NOT testing for a 'size=_1G + 1024 * 1024', because of the definition of 
+        the precisionbigmemtest decorator, which resets the value to 5147, based on 
+        the definition of test_support.real_max_memuse == 0
+        This is the case on my windows installation of python 2.7.3.
+        Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win32
+        And on my build of jython 2.7
+        Jython 2.7b1+ (default:d5a22e9b622a, Feb 9 2013, 20:36:27)
+        [Java HotSpot(TM) Client VM (Sun Microsystems Inc.)] on java1.6.0_29
+        """
         self.check_big_decompress_buffer(size, zlib.decompress)
 
 
@@ -389,13 +427,22 @@
         dco = zlib.decompressobj()
         self.assertEqual(dco.flush(), "") # Returns nothing
 
-    @unittest.skipIf(is_jython, "FIXME #1859: not working on Jython")
     def test_decompress_incomplete_stream(self):
         # This is 'foo', deflated
         x = 'x\x9cK\xcb\xcf\x07\x00\x02\x82\x01E'
         # For the record
         self.assertEqual(zlib.decompress(x), 'foo')
-        self.assertRaises(zlib.error, zlib.decompress, x[:-5])
+        if not is_jython:
+            # There is inconsistency between cpython zlib.decompress (which does not accept 
+            # incomplete streams) and zlib.decompressobj().decompress (which does accept
+            # incomplete streams, the whole point of this test)
+            # On jython, both zlib.decompress and zlib.decompressobject().decompress behave
+            # the same way: they both accept incomplete streams.
+            # Therefore, imposing this precondition is cpython specific
+            # and not appropriate on jython, which has consistent behaviour.
+            # http://bugs.python.org/issue8672
+            # http://bugs.jython.org/issue1859
+            self.assertRaises(zlib.error, zlib.decompress, x[:-5])
         # Omitting the stream end works with decompressor objects
         # (see issue #8672).
         dco = zlib.decompressobj()
@@ -473,6 +520,16 @@
 
     @precisionbigmemtest(size=_1G + 1024 * 1024, memuse=2)
     def test_big_decompress_buffer(self, size):
+        """
+        This is NOT testing for a 'size=_1G + 1024 * 1024', because of the definition of 
+        the precisionbigmemtest decorator, which resets the value to 5147, based on 
+        the definition of test_support.real_max_memuse == 0
+        This is the case on my windows installation of python 2.7.3.
+        Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win32
+        And on my build of jython 2.7
+        Jython 2.7b1+ (default:d5a22e9b622a, Feb 9 2013, 20:36:27)
+        [Java HotSpot(TM) Client VM (Sun Microsystems Inc.)] on java1.6.0_29
+        """
         d = zlib.decompressobj()
         decompress = lambda s: d.decompress(s) + d.flush()
         self.check_big_decompress_buffer(size, decompress)
diff --git a/Lib/zlib.py b/Lib/zlib.py
--- a/Lib/zlib.py
+++ b/Lib/zlib.py
@@ -61,16 +61,21 @@
     if level < Z_BEST_SPEED or level > Z_BEST_COMPRESSION:
         raise error, "Bad compression level"
     deflater = Deflater(level, 0)
-    string = _to_input(string)
-    deflater.setInput(string, 0, len(string))
-    deflater.finish()
-    return _get_deflate_data(deflater)
+    try:
+        string = _to_input(string)
+        deflater.setInput(string, 0, len(string))
+        deflater.finish()
+        return _get_deflate_data(deflater)
+    finally:
+        deflater.end()
 
 def decompress(string, wbits=0, bufsize=16384):
     inflater = Inflater(wbits < 0)
-    inflater.setInput(_to_input(string))
-
-    return _get_inflate_data(inflater)
+    try:
+        inflater.setInput(_to_input(string))
+        return _get_inflate_data(inflater)
+    finally:
+        inflater.end()
 
 class compressobj:
     # all jython uses wbits for is deciding whether to skip the header if it's negative
diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,15 @@
 Jython NEWS
 
-Jython 2.7a3
+Jython 2.7b2
   Bugs Fixed
+    - [ 1926 ] Adjust MutableSet.pop test so we do not need to skip it
+    - [ 2020 ] str.translate should delete characters in the second arg when table is None
+    - [ 1753 ] zlib doesn't call end() on compress and decompress
+
+Jython 2.7b1
+  Bugs Fixed
+    - [ 1716 ] xrange slicing raises NPE.
+    - [ 1968 ] Fixes for test_csv.py.
     - [ 1989 ] condition.notify_all() is missing.
     - [ 1994 ] threading.Event doesn't have is_set method fixed.
     - [ 1327 ] ThreadState needs API cleanup work
@@ -33,7 +41,26 @@
   Bugs Fixed
     - [ 1880 ] Sha 224 library not present in Jython
 
+Jython 2.5.4rc2
+  Bugs Fixed
+    - [ 2017 ] jython.bat script pollutes environment! (variables)
+    - [ 1899 ] Fix to platform.py to correctly avoid a warning message on Windows.
+    - [ 1988 ] API for threading.condition fails to accept *args for acquire
+    - [ 1753 ] zlib doesn't call end() on compress and decompress
+    - [ 1971 ] platform.py - 'NoneType' object has no attribute 'groups'
+    - [ 1988 ] API for threading.condition fails to accept *args for acquire
+
+Jython 2.5.4rc1
+  Bugs Fixed
+    - [ 1936 ] JBoss 7, vfs protocol in use for jarFileName in PySystemState.
+    - [ 1972 ] jython 2.5.3 sys.stdin.readline() hangs when jython launched as subprocess on Mac OS X.
+    - [ 1962 ] Interactive console in Jython 2.5.3 needs CRTL-D to execute command.
+    - [ 1676 ] NPE in defaultdict 
+    - [ 1481 ] jython throws java.lang.IllegalArgumentException instead of ValueError.
+    - [ 1716 ] xrange slicing raises NPE.
+
 Jython 2.5.3rc1
+  Bugs Fixed
     - [ 1952 ] __import__(): Handling of non-dict globals argument incompatible with CPython
     - [ 1900 ] Python imports from Java cause some Python imports to fail
 
diff --git a/README.txt b/README.txt
--- a/README.txt
+++ b/README.txt
@@ -1,15 +1,23 @@
-Welcome to Jython 2.7.0a2
-=========================
+Welcome to Jython 2.7b1
+=======================
 
-This is the second alpha release of the 2.7.0 version of Jython. Thanks to
+This is the first beta release of the 2.7 version of Jython. Thanks to
 Adconion Media Group (http://www.adconion.com/) for sponsoring this release.
-Thanks to all who contributed to Jython.
+Thanks to all who contribute to Jython.
 
-This release fixes a bug that left site-packages out of the path. This caused
-many problems, including a failure of ez_setup.py to work.
+Jython 2.7b1 brings us up to language level compatibility with the 2.7 version
+of CPython. We have focused largely on CPython compatibility, and so this
+release of Jython can run more pure Python apps then any previous release.
+Please see the NEWS file for detailed release notes. Some notable new features
+in this release are: a bytearray implementation, a buffer api, memoryview, a
+bz2 module.
+
+As the first beta release, this marks a change in the focus of development
+from adding features to bug fixing and on getting more of the test suite
+running properly.
 
 Please see the NEWS file for detailed release notes.
 
-The release was compiled on Ubuntu with JDK 6 and requires JDK 6 to run.
+The release was compiled on Ubuntu with JDK 7 and requires JDK 6 to run.
 
 Please try this out and report any bugs at http://bugs.jython.org.
diff --git a/build.xml b/build.xml
--- a/build.xml
+++ b/build.xml
@@ -9,16 +9,16 @@
 This build will create directories /build and /dist below basedir.
 
 
-Use case 2: full build for a release (using svn checkout)
+Use case 2: full build for a release (using hg checkout)
 ---------------------------------------------------------
-  - make sure you have access to the Jython Subversion repository
-    (https://jython.svn.sourceforge.net/svnroot/jython/trunk)
-  - override svn.main.dir in ant.properties (if necessary)
+  - make sure you have access to the Jython mercurial repository
+    (http://hg.python.org/jython)
+  - override ant.properties (if necessary)
   - call target 'full-build'
 
 This build will create a working directory named
-full_build/${svn.main.dir} at the same level as your local directories
-jython, sandbox and installer.  It will contain a big
+full_build at the same level as your local directories
+jython and installer.  It will contain a big
 jython_installer-${jython.version}.jar file suitable for installation.
 
 To build older releases, it may be necessary to use an older
@@ -58,45 +58,6 @@
 #debug=false
 #deprecation=off
 
-# - the svn main directory to build from; only needed for full-build
-# This e.g. could be one of:
-#   trunk
-#   branches/Release_2_2maint
-#   tags/Release_2_2rc3
-# meaning any directory just above the two directories:
-#   /installer
-#   /jython
-# svn.main.dir defaults to trunk
-#svn.main.dir=trunk
-
-# - the revision; only needed for a snapshot full-build
-# To create a snapshot build: uncomment the two revision lines, and indicate the correct revision (it has to be a number)
-# For 'normal' builds, this defaults to the latest revision on svn.main.dir (HEAD)
-#svn.revision=7114
-#snapshot.revision=${svn.revision}
-
-# - the directory containing libsvnjavahl-1.dll (on windows) and svnjavahl.jar; only needed for full-build
-# how to get these (for windows):
-#   - goto http://subversion.tigris.org/servlets/ProjectDocumentList
-#   - open the Releases folder
-#   - click on the Windows folder
-#   - download svn-win32-1.4.6_javahl.zip (or newer)
-#   - unzip the .dll and .jar into javahl.dir
-javahl.dir=C:/Programme/Subversion/javahl
-
-# - the directory containing the svnant related .jar files; only needed for full-build
-# the following .jar files (probably) are needed:
-#   - commons-lang-2.0.jar
-#   - jakarta-regexp-1.3.jar
-#   - svnant.jar
-#   - svnClientAdapter.jar
-#   - svnjavahl.jar
-# how to get these:
-#   - goto http://subclipse.tigris.org/servlets/ProjectDocumentList
-#   - click on the the svnant folder
-#   - download svnant-1.0.0.zip (or newer)
-#   - unzip the jar files from the /lib folder to svnant.jar.dir
-svnant.jar.dir=${basedir}/../externals/svnant-jars
         </echo>
     </target>
 
@@ -104,7 +65,7 @@
 
     <target name="developer-build" depends="prepare-output, pycompile" description="a local build for developers" />
 
-    <target name="full-build" depends="full-check, install" description="a full build with svn checkout" />
+    <target name="full-build" depends="full-check, installer" description="a full build with hg checkout" />
 
     <target name="needed-check" unless="full-build">
         <uptodate property="antlr.notneeded" targetfile="${gensrc.dir}/org/python/antlr/PythonParser.java">
@@ -123,23 +84,28 @@
         <property name="PY_RELEASE_LEVEL_SNAPSHOT" value="170"/> <!-- 0xAA -->
 
         <!-- The current version info -->
-        <property name="jython.version" value="2.7.0a2+"/>
-        <property name="jython.version.noplus" value="2.7.0a2"/>
+        <property name="jython.version" value="2.7b1+"/>
+        <property name="jython.version.noplus" value="2.7b1"/>
         <property name="jython.major_version" value="2"/>
         <property name="jython.minor_version" value="7"/>
         <property name="jython.micro_version" value="0"/>
-        <property name="jython.release_level" value="${PY_RELEASE_LEVEL_ALPHA}"/>
+        <property name="jython.release_level" value="${PY_RELEASE_LEVEL_BETA}"/>
         <!-- Usually zero, only used for alpha, beta and candidate versions
              where it must be greater than zero. -->
-        <property name="jython.release_serial" value="2"/>
+        <property name="jython.release_serial" value="1"/>
 		
     	<!--- Indy support -->
+        <!-- FIXME: hard coding to 1.6 for now until the compiler is updated. -->
+        <property name="jython.java.version" value="1.6"/>
+        <!--
     	<condition property="jython.use.invokedynamic">
 			<equals arg1="${java.specification.version}" arg2="1.7"/>
     	</condition>
     	<condition property="jython.java.version" value="1.7" else="1.6">
     		<isset property="jython.use.invokedynamic"/>
     	</condition>
+        -->
+
         <condition property="do.snapshot.build">
             <isset property="snapshot.revision" />
         </condition>
@@ -248,18 +214,14 @@
         <property file="${user.home}/ant.properties" />
         <property file="${basedir}/ant.properties" />
 
-        <property name="svnant.jar.dir" value="${basedir}/extlibs/svnant-jars" />
         <property name="cpptasks.jar.dir" value="${basedir}/extlibs/cpptasks/cpptasks.jar" />
 
         <!-- use this property to distinguish a full-build from a developer-build -->
         <property name="full-build" value="true" />
 
         <!-- predefined main directory for checkout -->
-        <property name="svn.main.dir" value="trunk" />
         <property name="hg.code.dir" value="jython" />
-        <property name="svn.installer.dir" value="installer" />
         <!-- predefined revision for checkout (this works for both trunk and release branches -->
-        <property name="svn.revision" value="HEAD" />
 
         <!-- properties work.dir and jython.base.dir are also definied in init, 
              so full-preinit must run first to work -->
@@ -272,15 +234,6 @@
             <istrue value="${has.repositories.connection}" />
         </condition>
 
-        <!-- classpath for svn ant task -->
-        <path id="svn.classpath">
-            <pathelement path="${java.class.path}" />
-            <fileset dir="${svnant.jar.dir}">
-              <include name="*.jar"/>
-            </fileset>
-        </path>
-    	<!-- taskdef for svn ant task -->
-    	<taskdef name="svn" classname="org.tigris.subversion.svnant.SvnTask" classpathref="svn.classpath" />
     </target>
     <target name="full-check" depends="full-preinit, init, dump-env">
         <!-- Require all of the optional jars for a full build -->
@@ -316,11 +269,8 @@
         <echo>debug              = '${debug}'</echo>
         <echo>nowarn             = '${nowarn}'</echo>
         <echo>--- properties (used for full-build only) ---</echo>
-        <echo>svn.main.dir       = '${svn.main.dir}'</echo>
-        <echo>svn.revision       = '${svn.revision}'</echo>
         <echo>checkout.dir   = '${checkout.dir}'</echo>
         <echo>javahl.dir         = '${javahl.dir}'</echo>
-        <echo>svnant.jar.dir     = '${svnant.jar.dir}'</echo>
         <echo>do.snapshot.build  = '${do.snapshot.build}'</echo>
         <echo>snapshot.revision  = '${snapshot.revision}'</echo>
         <echo>do.checkout        = '${do.checkout}'</echo>
@@ -407,15 +357,6 @@
             -->
             <arg line="clone http://hg.python.org/jython ${checkout.dir}/${hg.code.dir}"/>
         </exec>
-
-        <svn javahl="${javahl.dir}" >
-            <checkout url="https://jython.svn.sourceforge.net/svnroot/jython/${svn.main.dir}/${svn.installer.dir}" revision="${svn.revision}" destPath="${checkout.dir}/${svn.installer.dir}" />
-        </svn> 
-
-        <!-- checkout cpython license from the correct python maintenance branch -->
-        <svn javahl="${javahl.dir}" >
-               <checkout url="http://svn.python.org/projects/python/branches/release26-maint/" destPath="${checkout.dir}/python" recurse="false" />
-        </svn> 
     </target>
 
     <target name="check-hg">
@@ -423,8 +364,10 @@
       <condition property="hg-run">
         <and>
           <isset property="hg.present"/>
-          <!-- XXX: Might this work on Windows? -->
-          <isset property="os.family.unix"/>
+          <or>
+              <isset property="os.family.unix"/>
+              <isset property="os.family.windows"/>
+          </or>
         </and>
       </condition>
     </target>
@@ -475,7 +418,7 @@
         <!-- change README.txt version string, if so defined: used for
         snapshot builds. XXX: a bit broken for now-->
         <replace file="${jython.base.dir}/README.txt" token='2.7a1+'
-        value='2.7a${svn.revision}' />
+        value='2.7a${xxx.revision}' />
         <replace file="${jython.base.dir}/README.txt">
             <replacetoken>=======================</replacetoken>
             <replacevalue>--------------------------
@@ -683,7 +626,6 @@
                 <!-- info section. ATTN: no blanks, no '.' in the names -->
                 <section name="Build-Info">
                     <attribute name="version" value="${jython.version}" />
-                    <attribute name="svn-build" value="${do.checkout}" />
                     <attribute name="oracle" value="${oracle.present}" />
                     <attribute name="informix" value="${informix.present}" />
                     <attribute name="build-compiler" value="${build.compiler}" />
@@ -706,7 +648,7 @@
                 <!-- info section. ATTN: no blanks, no '.' in the names -->
                 <section name="Build-Info">
                     <attribute name="version" value="${jython.version}" />
-                    <attribute name="svn-build" value="${do.checkout}" />
+                    <attribute name="hg-build" value="${do.checkout}" />
                     <attribute name="oracle" value="${oracle.present}" />
                     <attribute name="informix" value="${informix.present}" />
                     <attribute name="build-compiler" value="${build.compiler}" />
@@ -742,7 +684,7 @@
               <!-- info section. ATTN: no blanks, no '.' in the names -->
               <section name="Build-Info">
                   <attribute name="version" value="${jython.version}" />
-                  <attribute name="svn-build" value="${do.checkout}" />
+                  <attribute name="hg-build" value="${do.checkout}" />
                   <attribute name="oracle" value="${oracle.present}" />
                   <attribute name="informix" value="${informix.present}" />
                   <attribute name="build-compiler" value="${build.compiler}" />
@@ -773,7 +715,7 @@
         </javadoc>
     </target>
 
-    <target name="all-jars" depends="jar-standalone, javadoc">
+    <target name="all-jars" depends="prepare, jar-standalone, javadoc, installer">
         <jar destfile="dist/${jython.javadoc.jar}">
             <fileset dir="${apidoc.dir}" includes="**"/>
         </jar>
@@ -894,17 +836,17 @@
         </copy>
     </target>
 
-    <!-- if install called by itself, make sure all the dependent targets run;
+    <!-- if installer called by itself, make sure all the dependent targets run;
          otherwise, redundant with full-check -->
-    <target name="install-init">
+    <target name="installer-init">
         <property name="full-build" value="true" />
     </target>
 
     <!-- wrap the build into the installer -->
-    <target name="install" depends="brand-readme-version, install-init, jar-standalone, javadoc, copy-full">
-        <property name="install.src.dir" value="${jython.base.dir}/../installer/src/java" />
-        <echo>compiling installer from ${install.src.dir}</echo>
-        <javac srcdir="${install.src.dir}"
+    <target name="installer" depends="brand-readme-version, installer-init, jar-standalone, javadoc, copy-full">
+        <property name="installer.src.dir" value="${jython.base.dir}/installer/src/java" />
+        <echo>compiling installer from ${installer.src.dir}</echo>
+        <javac srcdir="${installer.src.dir}"
                includes="org/**"
                destdir="${compile.dir}"
                target="${jdk.target.version}"
@@ -920,10 +862,10 @@
             	<include name="org/apache/commons/cli/*.class" />
             </fileset>
         </copy>
-        <copy file="${install.src.dir}/org/apache/LICENSE.txt" tofile="${dist.dir}/LICENSE_Apache.txt" preservelastmodified="true" />
+        <copy file="${installer.src.dir}/org/apache/LICENSE.txt" tofile="${dist.dir}/LICENSE_Apache.txt" preservelastmodified="true" />
         <echo>copy installer icon to ${dist.dir}</echo>
         <copy todir="${dist.dir}" preservelastmodified="true">
-            <fileset dir="${install.src.dir}">
+            <fileset dir="${installer.src.dir}">
                 <include name="**/*.png" />
                 <include name="**/*.template" />
                 <!-- check no /bin directory -->
@@ -931,14 +873,13 @@
             </fileset>
         </copy>
         <echo>building installer .jar file</echo>
-        <jar destfile="${work.dir}/jython_installer-${jython.version.noplus}.jar" update="true">
+        <jar destfile="${dist.dir}/jython-installer.jar" update="true">
             <fileset dir="${dist.dir}">
                 <exclude name="${jython.dev.jar}"/>
                 <exclude name="${jython.standalone.jar}"/>
                 <exclude name="${jython.javadoc.jar}"/>
                 <exclude name="${jython.sources.jar}"/>
                 <exclude name="callbacker_test.jar"/>
-                <exclude name="extlibs/svnant-jars/**" />
                 <exclude name="extlibs/antlr*.jar" />
                 <exclude name="extlibs/asm*.jar" />
                 <exclude name="extlibs/jarjar*.jar" />
@@ -950,14 +891,14 @@
             <manifest>
                 <attribute name="Main-Class" value="org.python.util.install.Installation" />
                 <attribute name="Built-By" value="${user.name}" />
-                <!-- section for the install program -->
+                <!-- section for the installer program -->
                 <section name="Jython">
                     <attribute name="version" value="${jython.version}" />
                     <attribute name="exclude-dirs" value="org;META-INF" />
                 </section>
                 <!-- info section. ATTN: no blanks, no '.' in the names -->
                 <section name="Build-Info">
-                    <attribute name="svn-build" value="${do.checkout}" />
+                    <attribute name="hg-build" value="${do.checkout}" />
                     <attribute name="oracle" value="${oracle.present}" />
                     <attribute name="informix" value="${informix.present}" />
                     <attribute name="build-compiler" value="${build.compiler}" />
diff --git a/extlibs/svnant-jars/svnClientAdapter.jar b/extlibs/svnant-jars/svnClientAdapter.jar
deleted file mode 100644
Binary file extlibs/svnant-jars/svnClientAdapter.jar has changed
diff --git a/extlibs/svnant-jars/svnant.jar b/extlibs/svnant-jars/svnant.jar
deleted file mode 100644
Binary file extlibs/svnant-jars/svnant.jar has changed
diff --git a/extlibs/svnant-jars/svnjavahl.jar b/extlibs/svnant-jars/svnjavahl.jar
deleted file mode 100644
Binary file extlibs/svnant-jars/svnjavahl.jar has changed
diff --git a/installer/src/java/org/apache/LICENSE.txt b/installer/src/java/org/apache/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/LICENSE.txt
@@ -0,0 +1,202 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
diff --git a/installer/src/java/org/apache/commons/cli/AlreadySelectedException.java b/installer/src/java/org/apache/commons/cli/AlreadySelectedException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/AlreadySelectedException.java
@@ -0,0 +1,81 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.commons.cli;
+
+/** 
+ * <p>Thrown when more than one option in an option group
+ * has been provided.</p>
+ *
+ * @author John Keyes ( john at integralsource.com )
+ * @see ParseException
+ */
+public class AlreadySelectedException extends ParseException {
+
+    /** 
+     * <p>Construct a new <code>AlreadySelectedException</code> 
+     * with the specified detail message.</p>
+     *
+     * @param message the detail message
+     */
+    public AlreadySelectedException( String message ) {
+        super( message );
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/BasicParser.java b/installer/src/java/org/apache/commons/cli/BasicParser.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/BasicParser.java
@@ -0,0 +1,92 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.commons.cli;
+
+/**
+ * The class BasicParser provides a very simple implementation of
+ * the {@link Parser#flatten(Options,String[],boolean) flatten} method.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @see Parser
+ */
+public class BasicParser extends Parser {
+
+    /**
+     * <p>A simple implementation of {@link Parser}'s abstract
+     * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p>
+     *
+     * <p><b>Note:</b> <code>options</code> and <code>stopAtNonOption</code>
+     * are not used in this <code>flatten</code> method.</p>
+     *
+     * @param options The command line {@link Options}
+     * @param arguments The command line arguments to be parsed
+     * @param stopAtNonOption Specifies whether to stop flattening
+     * when an non option is found.
+     * @return The <code>arguments</code> String array.
+     */
+    protected String[] flatten( Options options, 
+                                String[] arguments, 
+                                boolean stopAtNonOption )
+    {
+        // just echo the arguments
+        return arguments;
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/apache/commons/cli/CommandLine.java b/installer/src/java/org/apache/commons/cli/CommandLine.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/CommandLine.java
@@ -0,0 +1,328 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.commons.cli;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Map;
+
+/** 
+ * <p>Represents list of arguments parsed against
+ * a {@link Options} descriptor.<p>
+ *
+ * <p>It allows querying of a boolean {@link #hasOption(String opt)},
+ * in addition to retrieving the {@link #getOptionValue(String opt)}
+ * for options requiring arguments.</p>
+ *
+ * <p>Additionally, any left-over or unrecognized arguments,
+ * are available for further processing.</p>
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @author <a href="mailto:jstrachan at apache.org">James Strachan</a>
+ * @author John Keyes (john at integralsource.com)
+ */
+public class CommandLine {
+    
+    /** the unrecognised options/arguments */
+    private List args    = new LinkedList();
+
+    /** the processed options */
+    private Map options = new HashMap();
+
+    /** Map of unique options for ease to get complete list of options */
+    private Map hashcodeMap = new HashMap();
+
+    /** the processed options */
+    private Option[] optionsArray;
+
+    /**
+     * <p>Creates a command line.</p>
+     */
+    CommandLine() {
+    }
+    
+    /** 
+     * <p>Query to see if an option has been set.</p>
+     *
+     * @param opt Short name of the option
+     * @return true if set, false if not
+     */
+    public boolean hasOption(String opt) {
+        return options.containsKey( opt );
+    }
+
+    /** 
+     * <p>Query to see if an option has been set.</p>
+     *
+     * @param opt character name of the option
+     * @return true if set, false if not
+     */
+    public boolean hasOption( char opt ) {
+        return hasOption( String.valueOf( opt ) );
+    }
+
+    /**
+     * <p>Return the <code>Object</code> type of this <code>Option</code>.</p>
+     *
+     * @param opt the name of the option
+     * @return the type of this <code>Option</code>
+     */
+    public Object getOptionObject( String opt ) {
+        String res = getOptionValue( opt );
+        
+        Object type = ((Option)((List)options.get(opt)).iterator().next()).getType();
+        return res == null ? null : TypeHandler.createValue(res, type);
+    }
+
+    /**
+     * <p>Return the <code>Object</code> type of this <code>Option</code>.</p>
+     *
+     * @param opt the name of the option
+     * @return the type of opt
+     */
+    public Object getOptionObject( char opt ) {
+        return getOptionObject( String.valueOf( opt ) );
+    }
+
+    /** 
+     * <p>Retrieve the argument, if any, of this option.</p>
+     *
+     * @param opt the name of the option
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String getOptionValue( String opt ) {
+        String[] values = getOptionValues(opt);
+        return (values == null) ? null : values[0];
+    }
+
+    /** 
+     * <p>Retrieve the argument, if any, of this option.</p>
+     *
+     * @param opt the character name of the option
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String getOptionValue( char opt ) {
+        return getOptionValue( String.valueOf( opt ) );
+    }
+
+    /** 
+     * <p>Retrieves the array of values, if any, of an option.</p>
+     *
+     * @param opt string name of the option
+     * @return Values of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String[] getOptionValues( String opt ) {
+        List values = new java.util.ArrayList();
+
+        if( options.containsKey( opt ) ) {
+            List opts = (List)options.get( opt );
+            Iterator iter = opts.iterator();
+
+            while( iter.hasNext() ) {
+                Option optt = (Option)iter.next();
+                values.addAll( optt.getValuesList() );
+            }
+        }
+        return (values.size() == 0) ? null : (String[])values.toArray(new String[]{});
+    }
+
+    /** 
+     * <p>Retrieves the array of values, if any, of an option.</p>
+     *
+     * @param opt character name of the option
+     * @return Values of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String[] getOptionValues( char opt ) {
+        return getOptionValues( String.valueOf( opt ) );
+    }
+    
+    /** 
+     * <p>Retrieve the argument, if any, of an option.</p>
+     *
+     * @param opt name of the option
+     * @param defaultValue is the default value to be returned if the option is not specified
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise <code>defaultValue</code>.
+     */
+    public String getOptionValue( String opt, String defaultValue ) {
+        String answer = getOptionValue( opt );
+        return ( answer != null ) ? answer : defaultValue;
+    }
+    
+    /** 
+     * <p>Retrieve the argument, if any, of an option.</p>
+     *
+     * @param opt character name of the option
+     * @param defaultValue is the default value to be returned if the option is not specified
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise <code>defaultValue</code>.
+     */
+    public String getOptionValue( char opt, String defaultValue ) {
+        return getOptionValue( String.valueOf( opt ), defaultValue );
+    }
+
+    /** 
+     * <p>Retrieve any left-over non-recognized options and arguments</p>
+     *
+     * @return remaining items passed in but not parsed as an array
+     */
+    public String[] getArgs() {
+        String[] answer = new String[ args.size() ];
+        args.toArray( answer );
+        return answer;
+    }
+    
+    /** 
+     * <p>Retrieve any left-over non-recognized options and arguments</p>
+     *
+     * @return remaining items passed in but not parsed as a <code>List</code>.
+     */
+    public List getArgList() {
+        return args;
+    }
+    
+    /** 
+     * jkeyes
+     * - commented out until it is implemented properly
+     * <p>Dump state, suitable for debugging.</p>
+     *
+     * @return Stringified form of this object
+     */
+    /*
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        
+        buf.append( "[ CommandLine: [ options: " );
+        buf.append( options.toString() );
+        buf.append( " ] [ args: ");
+        buf.append( args.toString() );
+        buf.append( " ] ]" );
+        
+        return buf.toString();
+    }
+    */
+
+    /**
+     * <p>Add left-over unrecognized option/argument.</p>
+     *
+     * @param arg the unrecognised option/argument.
+     */
+    void addArg(String arg) {
+        args.add( arg );
+    }
+        
+    /**
+     * <p>Add an option to the command line.  The values of 
+     * the option are stored.</p>
+     *
+     * @param opt the processed option
+     */
+    void addOption( Option opt ) {
+        hashcodeMap.put( new Integer( opt.hashCode() ), opt );
+
+        String key = opt.getOpt();
+        if( " ".equals(key) ) {
+            key = opt.getLongOpt();
+        }
+
+        if( options.get( key ) != null ) {
+            ((java.util.List)options.get( key )).add( opt );
+        }
+        else {
+            options.put( key, new java.util.ArrayList() );
+            ((java.util.List)options.get( key ) ).add( opt );
+        }
+    }
+
+    /**
+     * <p>Returns an iterator over the Option members of CommandLine.</p>
+     *
+     * @return an <code>Iterator</code> over the processed {@link Option} 
+     * members of this {@link CommandLine}
+     */
+    public Iterator iterator( ) {
+        return hashcodeMap.values().iterator();
+    }
+
+    /**
+     * <p>Returns an array of the processed {@link Option}s.</p>
+     *
+     * @return an array of the processed {@link Option}s.
+     */
+    public Option[] getOptions( ) {
+        Collection processed = hashcodeMap.values();
+
+        // reinitialise array
+        optionsArray = new Option[ processed.size() ];
+
+        // return the array
+        return (Option[]) processed.toArray( optionsArray );
+    }
+
+}
diff --git a/installer/src/java/org/apache/commons/cli/CommandLineParser.java b/installer/src/java/org/apache/commons/cli/CommandLineParser.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/CommandLineParser.java
@@ -0,0 +1,97 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.commons.cli;
+
+/**
+ * A class that implements the <code>CommandLineParser</code> interface 
+ * can parse a String array according to the {@link Options} specified
+ * and return a {@link CommandLine}.
+ *
+ * @author John Keyes (john at integralsource.com)
+ */
+public interface CommandLineParser {
+    
+    /**
+     * Parse the arguments according to the specified options.
+     *
+     * @param options the specified Options
+     * @param arguments the command line arguments
+     * @return the list of atomic option and value tokens
+     * @throws ParseException if there are any problems encountered
+     * while parsing the command line tokens.
+     */
+    public CommandLine parse( Options options, String[] arguments )
+    throws ParseException;
+
+    /**
+     * Parse the arguments according to the specified options.
+     *
+     * @param options the specified Options
+     * @param arguments the command line arguments
+     * @param stopAtNonOption specifies whether to continue parsing the
+     * arguments if a non option is encountered.
+     * @return the list of atomic option and value tokens
+     * @throws ParseException if there are any problems encountered
+     * while parsing the command line tokens.
+     */
+    public CommandLine parse( Options options, String[] arguments, boolean stopAtNonOption )
+    throws ParseException;
+}
\ No newline at end of file
diff --git a/installer/src/java/org/apache/commons/cli/GnuParser.java b/installer/src/java/org/apache/commons/cli/GnuParser.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/GnuParser.java
@@ -0,0 +1,187 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.commons.cli;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * The class GnuParser provides an implementation of the 
+ * {@link Parser#flatten(Options,String[],boolean) flatten} method.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @see Parser
+ * @version $Revision: 2662 $
+ */
+public class GnuParser extends Parser {
+
+    /** holder for flattened tokens */
+    private ArrayList tokens = new ArrayList();
+
+    /**
+     * <p>Resets the members to their original state i.e. remove
+     * all of <code>tokens</code> entries.
+     */
+    private void init() {
+        tokens.clear();
+    }
+
+    /**
+     * <p>This flatten method does so using the following rules:
+     * <ol>
+     *  <li>If an {@link Option} exists for the first character of 
+     *  the <code>arguments</code> entry <b>AND</b> an {@link Option} 
+     *  does not exist for the whole <code>argument</code> then
+     *  add the first character as an option to the processed tokens
+     *  list e.g. "-D" and add the rest of the entry to the also.</li>
+     *  <li>Otherwise just add the token to the processed tokens list.
+     *  </li>
+     * </ol>
+     * </p>
+     */
+    protected String[] flatten( Options options, 
+                                String[] arguments, 
+                                boolean stopAtNonOption )
+    {
+        init();
+        boolean eatTheRest = false;
+        Option currentOption = null;
+
+        for( int i = 0; i < arguments.length; i++ ) {
+            if( "--".equals( arguments[i] ) ) {
+                eatTheRest = true;
+                tokens.add( "--" );
+            }
+            else if ( "-".equals( arguments[i] ) ) {
+                tokens.add( "-" );
+            }
+            else if( arguments[i].startsWith( "-" ) ) {
+                Option option = options.getOption( arguments[i] );
+
+                // this is not an Option
+                if( option == null ) {
+                    // handle special properties Option
+                    Option specialOption = options.getOption( arguments[i].substring(0,2) );
+                    if( specialOption != null ) {
+                        tokens.add( arguments[i].substring(0,2) );
+                        tokens.add( arguments[i].substring(2) );
+                    }
+                    else if( stopAtNonOption ) {
+                        eatTheRest = true;
+                        tokens.add( arguments[i] );
+                    }
+                    else {
+                        tokens.add( arguments[i] );
+                    }
+                }
+                else {
+                    currentOption = option;
+                    // special option
+                    Option specialOption = options.getOption( arguments[i].substring(0,2) );
+                    if( specialOption != null && option == null ) {
+                        tokens.add( arguments[i].substring(0,2) );
+                        tokens.add( arguments[i].substring(2) );
+                    }
+                    else if( currentOption != null && currentOption.hasArg() ) {
+                        if( currentOption.hasArg() ) {
+                            tokens.add( arguments[i] );
+                            currentOption= null;
+                        }
+                        else if ( currentOption.hasArgs() ) {
+                            tokens.add( arguments[i] );
+                        }
+                        else if ( stopAtNonOption ) {
+                            eatTheRest = true;
+                            tokens.add( "--" );
+                            tokens.add( arguments[i] );
+                        }
+                        else {
+                            tokens.add( arguments[i] );
+                        }
+                    } 
+                    else if (currentOption != null ) {
+                        tokens.add( arguments[i] );
+                    } 
+                    else if ( stopAtNonOption ) {
+                        eatTheRest = true;
+                        tokens.add( "--" );
+                        tokens.add( arguments[i] );
+                    }
+                    else {
+                        tokens.add( arguments[i] );
+                    }
+                }
+            }
+            else {
+                tokens.add( arguments[i] );
+            }
+
+            if( eatTheRest ) {
+                for( i++; i < arguments.length; i++ ) {
+                    tokens.add( arguments[i] );
+                }
+            }
+        }
+        return (String[])tokens.toArray( new String[] {} );
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/apache/commons/cli/HelpFormatter.java b/installer/src/java/org/apache/commons/cli/HelpFormatter.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/HelpFormatter.java
@@ -0,0 +1,542 @@
+/*
+ * $Header$
+ * $Revision: 2690 $
+ * $Date: 2006-03-25 23:39:57 -0800 (Sat, 25 Mar 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/** 
+ * A formatter of help messages for the current command line options
+ *
+ * @author Slawek Zachcial
+ * @author John Keyes (john at integralsource.com)
+ **/
+public class HelpFormatter
+{
+   // --------------------------------------------------------------- Constants
+
+   public static final int DEFAULT_WIDTH              = 74;
+   public static final int DEFAULT_LEFT_PAD           = 1;
+   public static final int DEFAULT_DESC_PAD           = 3;
+   public static final String DEFAULT_SYNTAX_PREFIX   = "usage: ";
+   public static final String DEFAULT_OPT_PREFIX      = "-";
+   public static final String DEFAULT_LONG_OPT_PREFIX = "--";
+   public static final String DEFAULT_ARG_NAME        = "arg";
+
+   // ------------------------------------------------------------------ Static
+
+   // -------------------------------------------------------------- Attributes
+
+   public int defaultWidth;
+   public int defaultLeftPad;
+   public int defaultDescPad;
+   public String defaultSyntaxPrefix;
+   public String defaultNewLine;
+   public String defaultOptPrefix;
+   public String defaultLongOptPrefix;
+   public String defaultArgName;
+
+   // ------------------------------------------------------------ Constructors
+   public HelpFormatter()
+   {
+      defaultWidth = DEFAULT_WIDTH;
+      defaultLeftPad = DEFAULT_LEFT_PAD;
+      defaultDescPad = DEFAULT_DESC_PAD;
+      defaultSyntaxPrefix = DEFAULT_SYNTAX_PREFIX;
+      defaultNewLine = System.getProperty("line.separator");
+      defaultOptPrefix = DEFAULT_OPT_PREFIX;
+      defaultLongOptPrefix = DEFAULT_LONG_OPT_PREFIX;
+      defaultArgName = DEFAULT_ARG_NAME;
+   }
+
+   // ------------------------------------------------------------------ Public
+
+   public void printHelp( String cmdLineSyntax,
+                          Options options )
+   {
+       printHelp( defaultWidth, cmdLineSyntax, null, options, null, false );
+   }
+
+   public void printHelp( String cmdLineSyntax,
+                          Options options,
+                          boolean autoUsage )
+   {
+       printHelp( defaultWidth, cmdLineSyntax, null, options, null, autoUsage );
+   }
+
+   public void printHelp( String cmdLineSyntax,
+                          String header,
+                          Options options,
+                          String footer )
+   {
+       printHelp( cmdLineSyntax, header, options, footer, false );
+   }
+
+   public void printHelp( String cmdLineSyntax,
+                          String header,
+                          Options options,
+                          String footer,
+                          boolean autoUsage )
+   {
+      printHelp(defaultWidth, cmdLineSyntax, header, options, footer, autoUsage );
+   }
+   
+   public void printHelp( int width,
+                          String cmdLineSyntax,
+                          String header,
+                          Options options,
+                          String footer )
+   {
+       printHelp( width, cmdLineSyntax, header, options, footer, false );
+   }
+
+   public void printHelp( int width,
+                          String cmdLineSyntax,
+                          String header,
+                          Options options,
+                          String footer,
+                          boolean autoUsage )
+   {
+      PrintWriter pw = new PrintWriter(System.out);
+      printHelp( pw, width, cmdLineSyntax, header,
+                 options, defaultLeftPad, defaultDescPad, footer, autoUsage );
+      pw.flush();
+   }
+   public void printHelp( PrintWriter pw,
+                          int width,
+                          String cmdLineSyntax,
+                          String header,
+                          Options options,
+                          int leftPad,
+                          int descPad,
+                          String footer )
+   throws IllegalArgumentException
+   {
+       printHelp( pw, width, cmdLineSyntax, header, options, leftPad, descPad, footer, false );
+   }
+
+   public void printHelp( PrintWriter pw,
+                          int width,
+                          String cmdLineSyntax,
+                          String header,
+                          Options options,
+                          int leftPad,
+                          int descPad,
+                          String footer,
+                          boolean autoUsage )
+      throws IllegalArgumentException
+   {
+      if ( cmdLineSyntax == null || cmdLineSyntax.length() == 0 )
+      {
+         throw new IllegalArgumentException("cmdLineSyntax not provided");
+      }
+
+      if ( autoUsage ) {
+          printUsage( pw, width, cmdLineSyntax, options );
+      }
+      else {
+          printUsage( pw, width, cmdLineSyntax );
+      }
+
+      if ( header != null && header.trim().length() > 0 )
+      {
+         printWrapped( pw, width, header );
+      }
+      printOptions( pw, width, options, leftPad, descPad );
+      if ( footer != null && footer.trim().length() > 0 )
+      {
+         printWrapped( pw, width, footer );
+      }
+   }
+
+   /**
+    * <p>Prints the usage statement for the specified application.</p>
+    *
+    * @param pw The PrintWriter to print the usage statement 
+    * @param width ??
+    * @param appName The application name
+    * @param options The command line Options
+    *
+    */
+   public void printUsage( PrintWriter pw, int width, String app, Options options ) 
+   {
+       // initialise the string buffer
+       StringBuffer buff = new StringBuffer( defaultSyntaxPrefix ).append( app ).append( " " );
+       
+       // create a list for processed option groups
+       ArrayList list = new ArrayList();
+
+       // temp variable
+       Option option;
+
+       // iterate over the options
+       for ( Iterator i = options.getOptions().iterator(); i.hasNext(); )
+       {
+           // get the next Option
+           option = (Option) i.next();
+
+           // check if the option is part of an OptionGroup
+           OptionGroup group = options.getOptionGroup( option );
+
+           // if the option is part of a group
+           if( group != null ) {
+               // if the group has not already been processed
+               if(!list.contains(group)) {
+    
+                   // add the group to the processed list
+                   list.add( group );
+    
+                   // get the names of the options from the OptionGroup
+                   Collection names = group.getNames();
+    
+                   buff.append( "[" ); 
+    
+                   // for each option in the OptionGroup
+                   for( Iterator iter = names.iterator(); iter.hasNext(); ) {
+                       buff.append( iter.next() );
+                       if( iter.hasNext() ) {
+                           buff.append( " | " );
+                       }
+                   }
+                   buff.append( "] " );
+               }
+           } else { // if the Option is not part of an OptionGroup
+               // if the Option is not a required option
+               if( !option.isRequired() ) {
+                   buff.append( "[" );
+               }
+               
+               if( !" ".equals( option.getOpt() ) ) {
+                   buff.append( "-" ).append( option.getOpt() );
+               }
+               else {
+                   buff.append( "--" ).append( option.getLongOpt() );
+               }
+
+               if( option.hasArg() ){
+                   buff.append( " " );
+               }
+
+               // if the Option has a value
+               if( option.hasArg() ) {
+                   buff.append( option.getArgName() );
+               }
+
+               // if the Option is not a required option
+               if( !option.isRequired() ) {
+                   buff.append( "]" );
+               }
+               buff.append( " " );
+           }
+       }
+
+       // call printWrapped
+       printWrapped( pw, width, buff.toString().indexOf(' ')+1,
+                     buff.toString() );
+   }
+
+   public void printUsage( PrintWriter pw, int width, String cmdLineSyntax )
+   {
+      int argPos = cmdLineSyntax.indexOf(' ') + 1;
+      printWrapped(pw, width, defaultSyntaxPrefix.length() + argPos,
+                   defaultSyntaxPrefix + cmdLineSyntax);
+   }
+
+   public void printOptions( PrintWriter pw, int width, Options options, int leftPad, int descPad )
+   {
+      StringBuffer sb = new StringBuffer();
+      renderOptions(sb, width, options, leftPad, descPad);
+      pw.println(sb.toString());
+   }
+
+   public void printWrapped( PrintWriter pw, int width, String text )
+   {
+      printWrapped(pw, width, 0, text);
+   }
+
+   public void printWrapped( PrintWriter pw, int width, int nextLineTabStop, String text )
+   {
+      StringBuffer sb = new StringBuffer(text.length());
+      renderWrappedText(sb, width, nextLineTabStop, text);
+      pw.println(sb.toString());
+   }
+
+   // --------------------------------------------------------------- Protected
+
+   protected StringBuffer renderOptions( StringBuffer sb,
+                                         int width,
+                                         Options options,
+                                         int leftPad,
+                                         int descPad )
+   {
+      final String lpad = createPadding(leftPad);
+      final String dpad = createPadding(descPad);
+
+      //first create list containing only <lpad>-a,--aaa where -a is opt and --aaa is
+      //long opt; in parallel look for the longest opt string
+      //this list will be then used to sort options ascending
+      int max = 0;
+      StringBuffer optBuf;
+      List prefixList = new ArrayList();
+      Option option;
+      List optList = options.helpOptions();
+      if (!options.isSortAsAdded()) {
+         Collections.sort(optList, new StringBufferComparator());
+      }
+      for ( Iterator i = optList.iterator(); i.hasNext(); )
+      {
+         option = (Option) i.next();
+         optBuf = new StringBuffer(8);
+
+         if (option.getOpt().equals(" "))
+         {
+             optBuf.append(lpad).append("   " + defaultLongOptPrefix).append(option.getLongOpt());
+         }
+         else
+         {
+             optBuf.append(lpad).append(defaultOptPrefix).append(option.getOpt());
+             if ( option.hasLongOpt() )
+             {
+                optBuf.append(',').append(defaultLongOptPrefix).append(option.getLongOpt());
+             }
+
+         }
+
+         if( option.hasArg() ) {
+             if( option.hasArgName() ) {
+                 optBuf.append(" <").append( option.getArgName() ).append( '>' );
+             }
+             else {
+                 optBuf.append(' ');
+             }
+         }
+
+         prefixList.add(optBuf);
+         max = optBuf.length() > max ? optBuf.length() : max;
+      }
+      int x = 0;
+      for ( Iterator i = optList.iterator(); i.hasNext(); )
+      {
+         option = (Option) i.next();
+         optBuf = new StringBuffer( prefixList.get( x++ ).toString() );
+
+         if ( optBuf.length() < max )
+         {
+             optBuf.append(createPadding(max - optBuf.length()));
+         }
+         optBuf.append( dpad );
+         
+         int nextLineTabStop = max + descPad;
+         renderWrappedText(sb, width, nextLineTabStop,
+                           optBuf.append(option.getDescription()).toString());
+         if ( i.hasNext() )
+         {
+             sb.append(defaultNewLine);
+         }
+      }
+
+      return sb;
+   }
+
+   protected StringBuffer renderWrappedText( StringBuffer sb,
+                                             int width,
+                                             int nextLineTabStop,
+                                             String text )
+   {
+      int pos = findWrapPos( text, width, 0);
+      if ( pos == -1 )
+      {
+         sb.append(rtrim(text));
+         return sb;
+      }
+      else
+      {
+         sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
+      }
+
+      //all following lines must be padded with nextLineTabStop space characters
+      final String padding = createPadding(nextLineTabStop);
+
+      while ( true )
+      {
+         text = padding + text.substring(pos).trim();
+         pos = findWrapPos( text, width, nextLineTabStop );
+         if ( pos == -1 )
+         {
+            sb.append(text);
+            return sb;
+         }
+
+         sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
+      }
+
+   }
+
+   /**
+    * Finds the next text wrap position after <code>startPos</code> for the text
+    * in <code>sb</code> with the column width <code>width</code>.
+    * The wrap point is the last postion before startPos+width having a whitespace
+    * character (space, \n, \r).
+    *
+    * @param sb text to be analyzed
+    * @param width width of the wrapped text
+    * @param startPos position from which to start the lookup whitespace character
+    * @return postion on which the text must be wrapped or -1 if the wrap position is at the end
+    *         of the text
+    */
+   protected int findWrapPos( String text, int width, int startPos )
+   {
+      int pos = -1;
+      // the line ends before the max wrap pos or a new line char found
+      if ( ((pos = text.indexOf('\n', startPos)) != -1 && pos <= width)  ||
+           ((pos = text.indexOf('\t', startPos)) != -1 && pos <= width) )
+      {
+         return pos;
+      }
+      else if ( (startPos + width) >= text.length() )
+      {
+         return -1;
+      }
+
+      //look for the last whitespace character before startPos+width
+      pos = startPos + width;
+      char c;
+      while ( pos >= startPos && (c = text.charAt(pos)) != ' ' && c != '\n' && c != '\r' )
+      {
+         --pos;
+      }
+      //if we found it - just return
+      if ( pos > startPos )
+      {
+         return pos;
+      }
+      else
+      {
+         //must look for the first whitespace chearacter after startPos + width
+         pos = startPos + width;
+         while ( pos <= text.length() && (c = text.charAt(pos)) != ' ' && c != '\n' && c != '\r' )
+         {
+            ++pos;
+         }
+         return pos == text.length() ? -1 : pos;
+      }
+   }
+
+   protected String createPadding(int len)
+   {
+      StringBuffer sb = new StringBuffer(len);
+      for ( int i = 0; i < len; ++i )
+      {
+         sb.append(' ');
+      }
+      return sb.toString();
+   }
+
+   protected String rtrim( String s )
+   {
+      if ( s == null || s.length() == 0 )
+      {
+         return s;
+      }
+
+      int pos = s.length();
+      while ( pos >= 0 && Character.isWhitespace(s.charAt(pos-1)) )
+      {
+         --pos;
+      }
+      return s.substring(0, pos);
+   }
+
+   // ------------------------------------------------------- Package protected
+   
+   // ----------------------------------------------------------------- Private
+   
+   // ----------------------------------------------------------- Inner classes
+
+    private static class StringBufferComparator
+    implements Comparator
+    {
+        public int compare( Object o1, Object o2 )
+        {
+            String str1 = stripPrefix(o1.toString());
+            String str2 = stripPrefix(o2.toString());
+            return (str1.compareTo(str2));
+        }
+
+        private String stripPrefix(String strOption)
+        {
+            // Strip any leading '-' characters
+            int iStartIndex = strOption.lastIndexOf('-');
+            if (iStartIndex == -1)
+            {
+              iStartIndex = 0;
+            }
+            return strOption.substring(iStartIndex);
+
+        }
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/MissingArgumentException.java b/installer/src/java/org/apache/commons/cli/MissingArgumentException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/MissingArgumentException.java
@@ -0,0 +1,82 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+/** 
+ * <p>Thrown when an option requiring an argument
+ * is not provided with an argument.</p>
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @see ParseException
+ */
+public class MissingArgumentException extends ParseException {
+    
+    /** 
+     * <p>Construct a new <code>MissingArgumentException</code> 
+     * with the specified detail message.</p>
+     *
+     * @param message the detail message
+     */
+    public MissingArgumentException( String message ) {
+        super( message );
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/MissingOptionException.java b/installer/src/java/org/apache/commons/cli/MissingOptionException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/MissingOptionException.java
@@ -0,0 +1,81 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+/** 
+ * <p>Thrown when a required option has not been provided.</p>
+ *
+ * @author John Keyes ( john at integralsource.com )
+ * @see ParseException
+ */
+public class MissingOptionException extends ParseException {
+    
+    /** 
+     * <p>Construct a new <code>MissingSelectedException</code> 
+     * with the specified detail message.</p>
+     *
+     * @param message the detail message
+     */
+    public MissingOptionException( String message ) {
+        super( message );
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/Option.java b/installer/src/java/org/apache/commons/cli/Option.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/Option.java
@@ -0,0 +1,575 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: Option.java 2662 2006-02-18 14:20:33Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import java.util.ArrayList;
+
+/** <p>Describes a single command-line option.  It maintains
+ * information regarding the short-name of the option, the long-name,
+ * if any exists, a flag indicating if an argument is required for
+ * this option, and a self-documenting description of the option.</p>
+ *
+ * <p>An Option is not created independantly, but is create through
+ * an instance of {@link Options}.<p>
+ *
+ * @see org.apache.commons.cli.Options
+ * @see org.apache.commons.cli.CommandLine
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @author <a href="mailto:jstrachan at apache.org">James Strachan</a>
+ * @version $Revision: 2662 $
+ */
+
+public class Option implements Cloneable {
+
+    /** constant that specifies the number of argument values has not been specified */
+    public final static int UNINITIALIZED = -1;
+    
+    /** constant that specifies the number of argument values is infinite */
+    public final static int UNLIMITED_VALUES = -2;
+    
+    /** opt the single character representation of the option */
+    private String opt;
+
+    /** longOpt is the long representation of the option */
+    private String longOpt;
+
+    /** hasArg specifies whether this option has an associated argument */
+    private boolean hasArg;
+
+    /** argName specifies the name of the argument for this option */
+    private String argName;
+
+    /** description of the option */
+    private String description;
+
+    /** required specifies whether this option is required to be present */
+    private boolean required;
+
+    /** specifies whether the argument value of this Option is optional */
+    private boolean optionalArg;
+
+    /** 
+     * numberOfArgs specifies the number of argument values this option 
+     * can have 
+     */
+    private int numberOfArgs = UNINITIALIZED;   
+
+    /** the type of this Option */
+    private Object type;
+
+    /** the list of argument values **/
+    private ArrayList values = new ArrayList();
+    
+    /** option char (only valid for single character options) */
+    private char id;
+
+    /** the character that is the value separator */
+    private char valuesep;
+
+    /**
+     * <p>Validates whether <code>opt</code> is a permissable Option
+     * shortOpt.  The rules that specify if the <code>opt</code>
+     * is valid are:</p>
+     * <ul>
+     *  <li><code>opt</code> is not NULL</li>
+     *  <li>a single character <code>opt</code> that is either
+     *  ' '(special case), '?', '@' or a letter</li>
+     *  <li>a multi character <code>opt</code> that only contains
+     *  letters.</li>
+     * </ul>
+     *
+     * @param opt The option string to validate
+     * @throws IllegalArgumentException if the Option is not valid.
+     */
+    private void validateOption( String opt ) 
+    throws IllegalArgumentException
+    {
+        // check that opt is not NULL
+        if( opt == null ) {
+            throw new IllegalArgumentException( "opt is null" );
+        }
+        // handle the single character opt
+        else if( opt.length() == 1 ) {
+            char ch = opt.charAt( 0 );
+            if ( !isValidOpt( ch ) ) {
+                throw new IllegalArgumentException( "illegal option value '" 
+                                                    + ch + "'" );
+            }
+            id = ch;
+        }
+        // handle the multi character opt
+        else {
+            char[] chars = opt.toCharArray();
+            for( int i = 0; i < chars.length; i++ ) {
+                if( !isValidChar( chars[i] ) ) {
+                    throw new IllegalArgumentException( "opt contains illegal character value '" + chars[i] + "'" );
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>Returns whether the specified character is a valid Option.</p>
+     *
+     * @param c the option to validate
+     * @return true if <code>c</code> is a letter, ' ', '?' or '@', otherwise false.
+     */
+    private boolean isValidOpt( char c ) {
+        return ( isValidChar( c ) || c == ' ' || c == '?' || c == '@' );
+    }
+
+    /**
+     * <p>Returns whether the specified character is a valid character.</p>
+     *
+     * @param c the character to validate
+     * @return true if <code>c</code> is a letter.
+     */
+    private boolean isValidChar( char c ) {
+        return Character.isJavaIdentifierPart( c );
+    }
+
+    /**
+     * <p>Returns the id of this Option.  This is only set when the
+     * Option shortOpt is a single character.  This is used for switch
+     * statements.</p>
+     *
+     * @return the id of this Option
+     */
+    public int getId( ) {
+        return id;
+    }
+
+    /**
+     * Creates an Option using the specified parameters.
+     *
+     * @param opt short representation of the option
+     * @param hasArg specifies whether the Option takes an argument or not
+     * @param description describes the function of the option
+     */
+    public Option( String opt, String description ) 
+    throws IllegalArgumentException
+    {
+        this( opt, null, false, description );
+    }
+
+    /**
+     * Creates an Option using the specified parameters.
+     *
+     * @param opt short representation of the option
+     * @param hasArg specifies whether the Option takes an argument or not
+     * @param description describes the function of the option
+     */
+    public Option( String opt, boolean hasArg, String description ) 
+    throws IllegalArgumentException
+    {
+        this( opt, null, hasArg, description );
+    }
+    
+    /**
+     * <p>Creates an Option using the specified parameters.</p>
+     *
+     * @param opt short representation of the option
+     * @param longOpt the long representation of the option
+     * @param hasArg specifies whether the Option takes an argument or not
+     * @param description describes the function of the option
+     */
+    public Option( String opt, String longOpt, boolean hasArg, String description ) 
+    throws IllegalArgumentException
+    {
+        // ensure that the option is valid
+        validateOption( opt );
+
+        this.opt          = opt;
+        this.longOpt      = longOpt;
+
+        // if hasArg is set then the number of arguments is 1
+        if( hasArg ) {
+            this.numberOfArgs = 1;
+        }
+
+        this.hasArg       = hasArg;
+        this.description  = description;
+    }
+    
+    /** <p>Retrieve the name of this Option.</p>
+     *
+     * <p>It is this String which can be used with
+     * {@link CommandLine#hasOption(String opt)} and
+     * {@link CommandLine#getOptionValue(String opt)} to check
+     * for existence and argument.<p>
+     *
+     * @return The name of this option
+     */
+    public String getOpt() {
+        return this.opt;
+    }
+
+    /**
+     * <p>Retrieve the type of this Option.</p>
+     * 
+     * @return The type of this option
+     */
+    public Object getType() {
+        return this.type;
+    }
+
+    /**
+     * <p>Sets the type of this Option.</p>
+     *
+     * @param type the type of this Option
+     */
+    public void setType( Object type ) {
+        this.type = type;
+    }
+    
+    /** 
+     * <p>Retrieve the long name of this Option.</p>
+     *
+     * @return Long name of this option, or null, if there is no long name
+     */
+    public String getLongOpt() {
+        return this.longOpt;
+    }
+
+    /**
+     * <p>Sets the long name of this Option.</p>
+     *
+     * @param longOpt the long name of this Option
+     */
+    public void setLongOpt( String longOpt ) {
+        this.longOpt = longOpt;
+    }
+
+    /**
+     * <p>Sets whether this Option can have an optional argument.</p>
+     *
+     * @param optionalArg specifies whether the Option can have
+     * an optional argument.
+     */
+    public void setOptionalArg( boolean optionalArg ) {
+        this.optionalArg = optionalArg;
+    }
+
+    /**
+     * @return whether this Option can have an optional argument
+     */
+    public boolean hasOptionalArg( ) {
+        return this.optionalArg;
+    }
+    
+    /** <p>Query to see if this Option has a long name</p>
+     *
+     * @return boolean flag indicating existence of a long name
+     */
+    public boolean hasLongOpt() {
+        return ( this.longOpt != null );
+    }
+    
+    /** <p>Query to see if this Option requires an argument</p>
+     *
+     * @return boolean flag indicating if an argument is required
+     */
+    public boolean hasArg() {
+        return this.numberOfArgs > 0 || numberOfArgs == UNLIMITED_VALUES;
+    }
+    
+    /** <p>Retrieve the self-documenting description of this Option</p>
+     *
+     * @return The string description of this option
+     */
+    public String getDescription() {
+        return this.description;
+    }
+
+     /** 
+      * <p>Query to see if this Option requires an argument</p>
+      *
+      * @return boolean flag indicating if an argument is required
+      */
+     public boolean isRequired() {
+         return this.required;
+     }
+
+     /**
+      * <p>Sets whether this Option is mandatory.</p>
+      *
+      * @param required specifies whether this Option is mandatory
+      */
+     public void setRequired( boolean required ) {
+         this.required = required;
+     }
+
+     /**
+      * <p>Sets the display name for the argument value.</p>
+      *
+      * @param argName the display name for the argument value.
+      */
+     public void setArgName( String argName ) {
+         this.argName = argName;
+     }
+
+     /**
+      * <p>Gets the display name for the argument value.</p>
+      *
+      * @return the display name for the argument value.
+      */
+     public String getArgName() {
+         return this.argName;
+     }
+
+     /**
+      * <p>Returns whether the display name for the argument value
+      * has been set.</p>
+      *
+      * @return if the display name for the argument value has been
+      * set.
+      */
+     public boolean hasArgName() {
+         return (this.argName != null && this.argName.length() > 0 );
+     }
+
+     /** 
+      * <p>Query to see if this Option can take many values</p>
+      *
+      * @return boolean flag indicating if multiple values are allowed
+      */
+     public boolean hasArgs() {
+         return ( this.numberOfArgs > 1 || this.numberOfArgs == UNLIMITED_VALUES );
+     }
+
+     /** 
+      * <p>Sets the number of argument values this Option can take.</p>
+      *
+      * @param num the number of argument values
+      */
+     public void setArgs( int num ) {
+         this.numberOfArgs = num;
+     }
+
+     /**
+      * <p>Sets the value separator.  For example if the argument value
+      * was a Java property, the value separator would be '='.</p>
+      *
+      * @param sep The value separator.
+      */
+     public void setValueSeparator( char sep ) {
+         this.valuesep = sep;
+     }
+
+     /**
+      * <p>Returns the value separator character.</p>
+      *
+      * @return the value separator character.
+      */
+     public char getValueSeparator() {
+         return this.valuesep;
+     }
+
+     /** 
+      * <p>Returns the number of argument values this Option can take.</p>
+      *
+      * @return num the number of argument values
+      */
+     public int getArgs( ) {
+         return this.numberOfArgs;
+     }
+
+    /** 
+     * <p>Dump state, suitable for debugging.</p>
+     *
+     * @return Stringified form of this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer().append("[ option: ");
+        
+        buf.append( this.opt );
+        
+        if ( this.longOpt != null ) {
+            buf.append(" ")
+            .append(this.longOpt);
+        }
+        
+        buf.append(" ");
+        
+        if ( hasArg ) {
+            buf.append( "+ARG" );
+        }
+        
+        buf.append(" :: ")
+        .append( this.description );
+        
+        if ( this.type != null ) {
+            buf.append(" :: ")
+            .append( this.type );
+        }
+
+        buf.append(" ]");
+        return buf.toString();
+    }
+
+    /**
+     * <p>Adds the specified value to this Option.</p>
+     * 
+     * @param value is a/the value of this Option
+     */
+    public boolean addValue( String value ) {
+
+        switch( numberOfArgs ) {
+            case UNINITIALIZED:
+                return false;
+            case UNLIMITED_VALUES:
+                if( getValueSeparator() > 0 ) {
+                    int index = 0;
+                    while( (index = value.indexOf( getValueSeparator() ) ) != -1 ) {
+                        this.values.add( value.substring( 0, index ) );
+                        value = value.substring( index+1 );
+                    }
+                }
+                this.values.add( value );
+                return true;
+            default:
+                if( getValueSeparator() > 0 ) {
+                    int index = 0;
+                    while( (index = value.indexOf( getValueSeparator() ) ) != -1 ) {
+                        if( values.size() > numberOfArgs-1 ) {
+                            return false;
+                        }
+                        this.values.add( value.substring( 0, index ) );
+                        value = value.substring( index+1 );
+                    }
+                }
+                if( values.size() > numberOfArgs-1 ) {
+                    return false;
+                }
+                this.values.add( value );
+                return true;
+        }
+    }
+
+    /**
+     * @return the value/first value of this Option or 
+     * <code>null</code> if there are no values.
+     */
+    public String getValue() {
+        return this.values.size()==0 ? null : (String)this.values.get( 0 );
+    }
+
+    /**
+     * @return the specified value of this Option or 
+     * <code>null</code> if there are no values.
+     */
+    public String getValue( int index ) 
+    throws IndexOutOfBoundsException
+    {
+        return ( this.values.size()==0 ) ? null : (String)this.values.get( index );
+    }
+
+    /**
+     * @return the value/first value of this Option or the 
+     * <code>defaultValue</code> if there are no values.
+     */
+    public String getValue( String defaultValue ) {
+        String value = getValue( );
+        return ( value != null ) ? value : defaultValue;
+    }
+
+    /**
+     * @return the values of this Option as a String array 
+     * or null if there are no values
+     */
+    public String[] getValues() {
+        return this.values.size()==0 ? null : (String[])this.values.toArray(new String[]{});
+    }
+
+    /**
+     * @return the values of this Option as a List
+     * or null if there are no values
+     */
+    public java.util.List getValuesList() {
+        return this.values;
+    }
+
+    /**
+     * @return a copy of this Option
+     */
+    public Object clone() {
+        Option option = new Option( getOpt(), getDescription() );
+        option.setArgs( getArgs() );
+        option.setOptionalArg( hasOptionalArg() );
+        option.setRequired( isRequired() );
+        option.setLongOpt( getLongOpt() );
+        option.setType( getType() );
+        option.setValueSeparator( getValueSeparator() );
+        return option;
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/OptionBuilder.java b/installer/src/java/org/apache/commons/cli/OptionBuilder.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/OptionBuilder.java
@@ -0,0 +1,368 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+/**
+ * <p>OptionBuilder allows the user to create Options using descriptive
+ * methods.</p>
+ * <p>Details on the Builder pattern can be found at 
+ * <a href="http://c2.com/cgi-bin/wiki?BuilderPattern">http://c2.com/cgi-bin/wiki?BuilderPattern</a>.</p>
+ *
+ * @author John Keyes ( john at integralsource.com )
+ * @since 1.0
+ */
+public class OptionBuilder {
+
+    /** long option */
+    private static String longopt;
+    /** option description */
+    private static String description;
+    /** argument name */
+    private static String argName;
+    /** is required? */
+    private static boolean required;
+    /** the number of arguments */
+    private static int numberOfArgs = Option.UNINITIALIZED;
+    /** option type */
+    private static Object type;
+    /** option can have an optional argument value */
+    private static boolean optionalArg;
+    /** value separator for argument value */
+    private static char valuesep;
+
+    /** option builder instance */
+    private static OptionBuilder instance = new OptionBuilder();
+
+    // private constructor
+    private OptionBuilder() {
+    }
+
+    /**
+     * <p>Resets the member variables to their default values.</p>
+     */
+    private static void reset() {
+        description = null;
+        argName = null;
+        longopt = null;
+        type = null;
+        required = false;
+        numberOfArgs = Option.UNINITIALIZED;
+
+        // PMM 9/6/02 - these were missing
+        optionalArg = false;
+        valuesep = (char) 0;
+    }
+
+    /**
+     * <p>The next Option created will have the following long option value.</p>
+     *
+     * @param longopt the long option value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withLongOpt( String longopt ) {
+        instance.longopt = longopt;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created will require an argument value.</p>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArg( ) {
+        instance.numberOfArgs = 1;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created will require an argument value if
+     * <code>hasArg</code> is true.</p>
+     *
+     * @param hasArg if true then the Option has an argument value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArg( boolean hasArg ) {
+        instance.numberOfArgs = ( hasArg == true ) ? 1 : Option.UNINITIALIZED;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created will have the specified argument value 
+     * name.</p>
+     *
+     * @param name the name for the argument value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withArgName( String name ) {
+        instance.argName = name;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created will be required.</p>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder isRequired( ) {
+        instance.required = true;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created uses <code>sep</code> as a means to
+     * separate argument values.</p>
+     *
+     * <b>Example:</b>
+     * <pre>
+     * Option opt = OptionBuilder.withValueSeparator( ':' )
+     *                           .create( 'D' );
+     *
+     * CommandLine line = parser.parse( args );
+     * String propertyName = opt.getValue( 0 );
+     * String propertyValue = opt.getValue( 1 );
+     * </pre>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withValueSeparator( char sep ) {
+        instance.valuesep = sep;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created uses '<code>=</code>' as a means to
+     * separate argument values.</p>
+     *
+     * <b>Example:</b>
+     * <pre>
+     * Option opt = OptionBuilder.withValueSeparator( )
+     *                           .create( 'D' );
+     *
+     * CommandLine line = parser.parse( args );
+     * String propertyName = opt.getValue( 0 );
+     * String propertyValue = opt.getValue( 1 );
+     * </pre>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withValueSeparator( ) {
+        instance.valuesep = '=';
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created will be required if <code>required</code>
+     * is true.</p>
+     *
+     * @param required if true then the Option is required
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder isRequired( boolean required ) {
+        instance.required = required;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created can have unlimited argument values.</p>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArgs( ) {
+        instance.numberOfArgs = Option.UNLIMITED_VALUES;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created can have <code>num</code> 
+     * argument values.</p>
+     *
+     * @param num the number of args that the option can have
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArgs( int num ) {
+        instance.numberOfArgs = num;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option can have an optional argument.</p>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasOptionalArg( ) {
+        instance.numberOfArgs = 1;
+        instance.optionalArg = true;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option can have an unlimited number of
+     * optional arguments.</p>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasOptionalArgs( ) {
+        instance.numberOfArgs = Option.UNLIMITED_VALUES;
+        instance.optionalArg = true;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option can have the specified number of 
+     * optional arguments.</p>
+     *
+     * @param numArgs - the maximum number of optional arguments
+     * the next Option created can have.
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasOptionalArgs( int numArgs ) {
+        instance.numberOfArgs = numArgs;
+        instance.optionalArg = true;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created will have a value that will be an instance 
+     * of <code>type</code>.</p>
+     *
+     * @param type the type of the Options argument value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withType( Object type ) {
+        instance.type = type;
+        return instance;
+    }
+
+    /**
+     * <p>The next Option created will have the specified description</p>
+     *
+     * @param description a description of the Option's purpose
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withDescription( String description ) {
+        instance.description = description;
+        return instance;
+    }
+
+    /**
+     * <p>Create an Option using the current settings and with 
+     * the specified Option <code>char</code>.</p>
+     *
+     * @param opt the character representation of the Option
+     * @return the Option instance
+     * @throws IllegalArgumentException if <code>opt</code> is not
+     * a valid character.  See Option.
+     */
+    public static Option create( char opt )
+    throws IllegalArgumentException
+    {
+        return create( String.valueOf( opt ) );
+    }
+
+    /**
+     * <p>Create an Option using the current settings</p>
+     *
+     * @return the Option instance
+     * @throws IllegalArgumentException if <code>longOpt</code> has
+     * not been set.  
+     */
+    public static Option create() 
+    throws IllegalArgumentException
+    {
+        if( longopt == null ) {
+            throw new IllegalArgumentException( "must specify longopt" );
+        }
+
+        return create( " " );
+    }
+
+    /**
+     * <p>Create an Option using the current settings and with 
+     * the specified Option <code>char</code>.</p>
+     *
+     * @param opt the <code>java.lang.String</code> representation 
+     * of the Option
+     * @return the Option instance
+     * @throws IllegalArgumentException if <code>opt</code> is not
+     * a valid character.  See Option.
+     */
+    public static Option create( String opt ) 
+    throws IllegalArgumentException
+    {
+        // create the option
+        Option option = new Option( opt, description );
+
+        // set the option properties
+        option.setLongOpt( longopt );
+        option.setRequired( required );
+        option.setOptionalArg( optionalArg );
+        option.setArgs( numberOfArgs );
+        option.setType( type );
+        option.setValueSeparator( valuesep );
+        option.setArgName( argName );
+        // reset the OptionBuilder properties
+        instance.reset();
+
+        // return the Option instance
+        return option;
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/apache/commons/cli/OptionGroup.java b/installer/src/java/org/apache/commons/cli/OptionGroup.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/OptionGroup.java
@@ -0,0 +1,187 @@
+/*
+ * $Header$
+ * $Revision: 2690 $
+ * $Date: 2006-03-25 23:39:57 -0800 (Sat, 25 Mar 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A group of mutually exclusive options.
+ * @author John Keyes ( john at integralsource.com )
+ * @version $Revision: 2690 $
+ */
+public class OptionGroup {
+    
+    /** hold the options in added order */
+    private List optionList = new ArrayList();
+
+    /** the name of the selected option */
+    private String selected;
+
+    /** specified whether this group is required */
+    private boolean required;
+
+    /**
+     * add <code>opt</code> to this group
+     *
+     * @param opt the option to add to this group
+     * @return this option group with opt added
+     */
+    public OptionGroup addOption(Option opt) {
+        optionList.add(opt);
+        return this;
+    }
+
+    /**
+     * @return the names of the options in this group as a 
+     * <code>Collection</code>
+     */
+    public Collection getNames() {
+        List namesList = new ArrayList();
+        Iterator optionsIterator = optionList.iterator();
+        while (optionsIterator.hasNext()) {
+            Option option = (Option) optionsIterator.next();
+            namesList.add("-" + option.getOpt());
+        }
+        return namesList;
+    }
+
+    /**
+     * @return the options in this group as a <code>Collection</code>
+     */
+    public Collection getOptions() {
+        return optionList;
+    }
+
+    /**
+     * set the selected option of this group to <code>name</code>.
+     * @param opt the option that is selected
+     * @throws AlreadySelectedException if an option from this group has 
+     * already been selected.
+     */
+    public void setSelected(Option opt) throws AlreadySelectedException {
+        // if no option has already been selected or the 
+        // same option is being reselected then set the
+        // selected member variable
+
+        if ( this.selected == null || this.selected.equals( opt.getOpt() ) ) {
+            this.selected = opt.getOpt();
+        }
+        else {
+            throw new AlreadySelectedException( "an option from this group has " + 
+                                                "already been selected: '" + 
+                                                selected + "'");
+        }
+    }
+
+    /**
+     * @return the selected option name
+     */
+    public String getSelected() {
+        return selected;
+    }
+
+    /**
+     * @param required specifies if this group is required
+     */
+    public void setRequired( boolean required ) {
+        this.required = required;
+    }
+
+    /**
+     * Returns whether this option group is required.
+     *
+     * @returns whether this option group is required
+     */
+    public boolean isRequired() {
+        return this.required;
+    }
+
+    /**
+     * <p>Returns the stringified version of this OptionGroup.</p>
+     * @return the stringified representation of this group
+     */
+    public String toString() {
+        StringBuffer buff = new StringBuffer();
+
+        Iterator iter = getOptions().iterator();
+
+        buff.append( "[" );
+        while( iter.hasNext() ) {
+            Option option = (Option)iter.next();
+
+            buff.append( "-" );
+            buff.append( option.getOpt() );
+            buff.append( " " );
+            buff.append( option.getDescription( ) );
+
+            if( iter.hasNext() ) {
+                buff.append( ", " );
+            }
+        }
+        buff.append( "]" );
+
+        return buff.toString();
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/Options.java b/installer/src/java/org/apache/commons/cli/Options.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/Options.java
@@ -0,0 +1,331 @@
+/*
+ * $Header$
+ * $Revision: 2694 $
+ * $Date: 2006-03-27 11:45:13 -0800 (Mon, 27 Mar 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/** <p>Main entry-point into the library.</p>
+ *
+ * <p>Options represents a collection of {@link Option} objects, which
+ * describe the possible options for a command-line.<p>
+ *
+ * <p>It may flexibly parse long and short options, with or without
+ * values.  Additionally, it may parse only a portion of a commandline,
+ * allowing for flexible multi-stage parsing.<p>
+ *
+ * @see org.apache.commons.cli.CommandLine
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @author <a href="mailto:jstrachan at apache.org">James Strachan</a>
+ * @version $Revision: 2694 $
+ */
+public class Options {
+
+    /** a map of the options with the character key */
+    private Map  shortOpts    = new HashMap();
+
+    /** a map of the options with the long key */
+    private Map  longOpts     = new HashMap();
+
+    /** a list of the required options */
+    private List requiredOpts = new ArrayList();
+    
+    /** a map of the option groups */
+    private Map optionGroups  = new HashMap();
+    
+    /** a list of all options, in sequence of addition, only in use when isSortAsAdded() */
+    private List _addedOpts = new ArrayList();
+
+    /** flag for the sort behaviour */
+    private boolean _sortAsAdded;
+    
+    /**
+     * Construct a new Options descriptor
+     */
+    public Options() {
+        setSortAsAdded(false);
+    }
+
+    /**
+     * Set the sort sequence: <code>true</code> means in sequence of addition, <code>false</code> will use the
+     * default (undefined) sorting
+     */
+    public void setSortAsAdded(boolean sortAsAdded) {
+        _sortAsAdded = sortAsAdded;
+    }
+
+    /**
+     * <p>Add the specified option group.</p>
+     *
+     * @param group the OptionGroup that is to be added
+     * @return the resulting Options instance
+     */
+    public Options addOptionGroup( OptionGroup group ) {
+        Iterator options = group.getOptions().iterator();
+
+        if( group.isRequired() ) {
+            requiredOpts.add( group );
+        }
+
+        while( options.hasNext() ) {
+            Option option = (Option)options.next();
+            // an Option cannot be required if it is in an
+            // OptionGroup, either the group is required or
+            // nothing is required
+            option.setRequired( false );
+            addOption( option );
+
+            optionGroups.put( option.getOpt(), group );
+        }
+
+        return this;
+    }
+
+    /** <p>Add an option that only contains a short-name</p>
+     * <p>It may be specified as requiring an argument.</p>
+     *
+     * @param opt Short single-character name of the option.
+     * @param hasArg flag signally if an argument is required after this option
+     * @param description Self-documenting description
+     * @return the resulting Options instance
+     */
+    public Options addOption(String opt, boolean hasArg, String description) {
+        addOption( opt, null, hasArg, description );
+        return this;
+    }
+    
+    /** <p>Add an option that contains a short-name and a long-name</p>
+     * <p>It may be specified as requiring an argument.</p>
+     *
+     * @param opt Short single-character name of the option.
+     * @param longOpt Long multi-character name of the option.
+     * @param hasArg flag signally if an argument is required after this option
+     * @param description Self-documenting description
+     * @return the resulting Options instance
+     */
+    public Options addOption(String opt, String longOpt, boolean hasArg, String description) {
+        addOption( new Option( opt, longOpt, hasArg, description ) );        
+        return this;
+    }
+
+    /**
+     * <p>Adds an option instance</p>
+     *
+     * @param opt the option that is to be added 
+     * @return the resulting Options instance
+     */
+    public Options addOption(Option opt)  {
+        String shortOpt = "-" + opt.getOpt();
+        
+        // add it to the long option list
+        if ( opt.hasLongOpt() ) {
+            longOpts.put( "--" + opt.getLongOpt(), opt );
+        }
+        
+        // if the option is required add it to the required list
+        if ( opt.isRequired() ) {
+            requiredOpts.add( shortOpt );
+        }
+
+        shortOpts.put( shortOpt, opt );
+        
+        if (isSortAsAdded()) {
+            _addedOpts.add(opt);
+        }
+        
+        return this;
+    }
+    
+    /** <p>Retrieve a read-only list of options in this set</p>
+     *
+     * @return read-only Collection of {@link Option} objects in this descriptor
+     */
+    public Collection getOptions() {
+        if (isSortAsAdded()) {
+            return _addedOpts;
+        } else {
+            List opts = new ArrayList(shortOpts.values());
+
+            // now look through the long opts to see if there are any Long-opt
+            // only options
+            Iterator iter = longOpts.values().iterator();
+            while (iter.hasNext()) {
+                Object item = iter.next();
+                if (!opts.contains(item)) {
+                    opts.add(item);
+                }
+            }
+            return Collections.unmodifiableCollection(opts);
+        }
+    }
+    
+    /**
+     * @return true if options should be sorted in sequence of addition.
+     */
+    public boolean isSortAsAdded() {
+        return _sortAsAdded;
+    }
+
+    /**
+     * <p>Returns the Options for use by the HelpFormatter.</p>
+     *
+     * @return the List of Options
+     */
+    List helpOptions() {
+        if (isSortAsAdded()) {
+            return _addedOpts;
+        } else {
+            return new ArrayList(shortOpts.values());
+        }
+    }
+
+    /** <p>Returns the required options as a 
+     * <code>java.util.Collection</code>.</p>
+     *
+     * @return Collection of required options
+     */
+    public List getRequiredOptions() {
+        return requiredOpts;
+    }
+    
+    /** <p>Retrieve the named {@link Option}</p>
+     *
+     * @param opt short or long name of the {@link Option}
+     * @return the option represented by opt
+     */
+    public Option getOption( String opt ) {
+
+        Option option = null;
+
+        // short option
+        if( opt.length() == 1 ) {
+            option = (Option)shortOpts.get( "-" + opt );
+        }
+        // long option
+        else if( opt.startsWith( "--" ) ) {
+            option = (Option)longOpts.get( opt );
+        }
+        // a just-in-case
+        else {
+            option = (Option)shortOpts.get( opt );
+        }
+
+        return (option == null) ? null : (Option)option.clone();
+    }
+
+    /** 
+     * <p>Returns whether the named {@link Option} is a member
+     * of this {@link Options}</p>
+     *
+     * @param opt short or long name of the {@link Option}
+     * @return true if the named {@link Option} is a member
+     * of this {@link Options}
+     */
+    public boolean hasOption( String opt ) {
+
+        // short option
+        if( opt.length() == 1 ) {
+            return shortOpts.containsKey( "-" + opt );
+        }
+        // long option
+        else if( opt.startsWith( "--" ) ) {
+            return longOpts.containsKey( opt );
+        }
+        // a just-in-case
+        else {
+            return shortOpts.containsKey( opt );
+        }
+    }
+
+    /** <p>Returns the OptionGroup the <code>opt</code>
+     * belongs to.</p>
+     * @param opt the option whose OptionGroup is being queried.
+     *
+     * @return the OptionGroup if <code>opt</code> is part
+     * of an OptionGroup, otherwise return null
+     */
+    public OptionGroup getOptionGroup( Option opt ) {
+        return (OptionGroup)optionGroups.get( opt.getOpt() );
+    }
+    
+    /** <p>Dump state, suitable for debugging.</p>
+     *
+     * @return Stringified form of this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        
+        buf.append("[ Options: [ short ");
+        buf.append( shortOpts.toString() );
+        buf.append( " ] [ long " );
+        buf.append( longOpts );
+        buf.append( " ]");
+        
+        return buf.toString();
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/ParseException.java b/installer/src/java/org/apache/commons/cli/ParseException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/ParseException.java
@@ -0,0 +1,82 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+/** 
+ * <p>Base for Exceptions thrown during parsing of a command-line.</p>
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @version $Revision: 2662 $
+ */
+public class ParseException extends Exception 
+{
+    
+    /** 
+     * <p>Construct a new <code>ParseException</code> 
+     * with the specified detail message.</p>
+     *
+     * @param message the detail message
+     */
+    public ParseException( String message ) {
+        super( message );
+    }
+}
diff --git a/installer/src/java/org/apache/commons/cli/Parser.java b/installer/src/java/org/apache/commons/cli/Parser.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/Parser.java
@@ -0,0 +1,282 @@
+/*
+ * $Header$
+ * $Revision: 2665 $
+ * $Date: 2006-02-18 14:24:36 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * <p><code>Parser</code> creates {@link CommandLine}s.</p>
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @see Parser
+ * @version $Revision: 2665 $
+ */
+public abstract class Parser implements CommandLineParser {
+
+    /** commandline instance */
+    private CommandLine cmd;
+    /** current Options */
+    private Options options;
+    /** list of required options strings */
+    private List requiredOptions;
+
+    /**
+     * <p>Subclasses must implement this method to reduce
+     * the <code>arguments</code> that have been passed to the parse 
+     * method.</p>
+     *
+     * @param opts The Options to parse the arguments by.
+     * @param args The arguments that have to be flattened.
+     * @param stopAtNonOption specifies whether to stop 
+     * flattening when a non option has been encountered
+     * @return a String array of the flattened arguments
+     */
+    abstract protected String[] flatten( Options opts, 
+                                         String[] arguments, 
+                                         boolean stopAtNonOption );
+
+    /**
+     * <p>Parses the specified <code>arguments</code> 
+     * based on the specifed {@link Options}.</p>
+     *
+     * @param options the <code>Options</code>
+     * @param arguments the <code>arguments</code>
+     * @return the <code>CommandLine</code>
+     * @throws ParseException if an error occurs when parsing the
+     * arguments.
+     */
+    public CommandLine parse( Options options, String[] arguments ) 
+    throws ParseException 
+    {
+        return parse( options, arguments, false );
+    }
+
+    /**
+     * <p>Parses the specified <code>arguments</code> 
+     * based on the specifed {@link Options}.</p>
+     *
+     * @param options the <code>Options</code>
+     * @param arguments the <code>arguments</code>
+     * @param stopAtNonOption specifies whether to stop 
+     * interpreting the arguments when a non option has 
+     * been encountered and to add them to the CommandLines
+     * args list.
+     * @return the <code>CommandLine</code>
+     * @throws ParseException if an error occurs when parsing the
+     * arguments.
+     */
+    public CommandLine parse( Options opts, 
+                              String[] arguments, 
+                              boolean stopAtNonOption ) 
+    throws ParseException 
+    {
+        // initialise members
+        options = opts;
+        requiredOptions = options.getRequiredOptions();
+        cmd = new CommandLine();
+
+        boolean eatTheRest = false;
+
+        List tokenList = Arrays.asList( flatten( opts, arguments, stopAtNonOption ) );
+        ListIterator iterator = tokenList.listIterator();
+
+        // process each flattened token
+        while( iterator.hasNext() ) {
+            String t = (String)iterator.next();
+
+            // the value is the double-dash
+            if( "--".equals( t ) ) {
+                eatTheRest = true;
+            }
+            // the value is a single dash
+            else if( "-".equals( t ) ) {
+                if( stopAtNonOption ) {
+                    eatTheRest = true;
+                }
+                else {
+                    cmd.addArg(t );
+                }
+            }
+            // the value is an option
+            else if( t.startsWith( "-" ) ) {
+                if ( stopAtNonOption && !options.hasOption( t ) ) {
+                    eatTheRest = true;
+                    cmd.addArg( t );
+                }
+                else {
+                    processOption( t, iterator );
+                }
+            }
+            // the value is an argument
+            else {
+                cmd.addArg( t );
+                if( stopAtNonOption ) {
+                    eatTheRest = true;
+                }
+            }
+
+            // eat the remaining tokens
+            if( eatTheRest ) {
+                while( iterator.hasNext() ) {
+                    String str = (String)iterator.next();
+                    // ensure only one double-dash is added
+                    if( !"--".equals( str ) ) {
+                        cmd.addArg( str );
+                    }
+                }
+            }
+        }
+        checkRequiredOptions();
+        return cmd;
+    }
+
+    /**
+     * <p>Throws a {@link MissingOptionException} if all of the
+     * required options are no present.</p>
+     */
+    private void checkRequiredOptions()
+    throws MissingOptionException 
+    {
+
+        // if there are required options that have not been
+        // processsed
+        if( requiredOptions.size() > 0 ) {
+            Iterator iter = requiredOptions.iterator();
+            StringBuffer buff = new StringBuffer();
+
+            // loop through the required options
+            while( iter.hasNext() ) {
+                buff.append( iter.next() );
+            }
+
+            throw new MissingOptionException( buff.toString() );
+        }
+    }
+
+    public void processArgs( Option opt, ListIterator iter ) 
+    throws ParseException
+    {
+        // loop until an option is found
+        while( iter.hasNext() ) {
+            String var = (String)iter.next();
+
+            // found an Option
+            if( options.hasOption( var ) ) {
+                iter.previous();
+                break;
+            }
+            // found a value
+            else if( !opt.addValue( var ) ) {
+                iter.previous();
+                break;
+            }
+        }
+
+        if( opt.getValues() == null && !opt.hasOptionalArg() ) {
+            throw new MissingArgumentException( "no argument for option: " + opt.getOpt() + " / " + opt.getLongOpt() );
+        }
+    }
+
+    private void processOption( String arg, ListIterator iter ) 
+    throws ParseException
+    {
+        // get the option represented by arg
+        Option opt = null;
+
+        boolean hasOption = options.hasOption( arg );
+
+        // if there is no option throw an UnrecognisedOptionException
+        if( !hasOption ) {
+            throw new UnrecognizedOptionException("Unrecognized option: " + arg);
+        }
+        else {
+            opt = (Option) options.getOption( arg );
+        }
+
+        // if the option is a required option remove the option from
+        // the requiredOptions list
+        if ( opt.isRequired() ) {
+            requiredOptions.remove( "-" + opt.getOpt() );
+        }
+
+        // if the option is in an OptionGroup make that option the selected
+        // option of the group
+        if ( options.getOptionGroup( opt ) != null ) {
+            OptionGroup group = ( OptionGroup ) options.getOptionGroup( opt );
+            if( group.isRequired() ) {
+                requiredOptions.remove( group );
+            }
+            group.setSelected( opt );
+        }
+
+        // if the option takes an argument value
+        if ( opt.hasArg() ) {
+            processArgs( opt, iter );
+        }
+
+        // set the option on the command line
+        cmd.addOption( opt );
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/apache/commons/cli/PatternOptionBuilder.java b/installer/src/java/org/apache/commons/cli/PatternOptionBuilder.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/PatternOptionBuilder.java
@@ -0,0 +1,204 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+/** 
+ * Allows Options to be created from a single String.
+ *
+ *
+ * @author Henri Yandell (bayard @ generationjava.com)
+ * @version $Revision: 2662 $
+ */
+public class PatternOptionBuilder {
+
+    /// TODO: These need to break out to OptionType and also to be pluggable.
+
+    /** String class */
+    public static final Class STRING_VALUE        = java.lang.String.class;
+    /** Object class */
+    public static final Class OBJECT_VALUE        = java.lang.Object.class;
+    /** Number class */
+    public static final Class NUMBER_VALUE        = java.lang.Number.class;
+    /** Date class */
+    public static final Class DATE_VALUE          = java.util.Date.class;
+    /** Class class */
+    public static final Class CLASS_VALUE         = java.lang.Class.class;
+
+/// can we do this one?? 
+// is meant to check that the file exists, else it errors.
+// ie) it's for reading not writing.
+    /** FileInputStream class */
+    public static final Class EXISTING_FILE_VALUE = java.io.FileInputStream.class;
+    /** File class */
+    public static final Class FILE_VALUE          = java.io.File.class;
+    /** File array class */
+    public static final Class FILES_VALUE         = java.io.File[].class;
+    /** URL class */
+    public static final Class URL_VALUE           = java.net.URL.class;
+
+    /**
+     * <p>Retrieve the class that <code>ch</code> represents.</p>
+     *
+     * @param ch the specified character
+     * @return The class that <code>ch</code> represents
+     */
+    public static Object getValueClass(char ch) {
+        if (ch == '@') {
+            return PatternOptionBuilder.OBJECT_VALUE;
+        } else if (ch == ':') {
+            return PatternOptionBuilder.STRING_VALUE;
+        } else if (ch == '%') {
+            return PatternOptionBuilder.NUMBER_VALUE;
+        } else if (ch == '+') {
+            return PatternOptionBuilder.CLASS_VALUE;
+        } else if (ch == '#') {
+            return PatternOptionBuilder.DATE_VALUE;
+        } else if (ch == '<') {
+            return PatternOptionBuilder.EXISTING_FILE_VALUE;
+        } else if (ch == '>') {
+            return PatternOptionBuilder.FILE_VALUE;
+        } else if (ch == '*') {
+            return PatternOptionBuilder.FILES_VALUE;
+        } else if (ch == '/') {
+            return PatternOptionBuilder.URL_VALUE;
+        }
+        return null;
+    }
+ 
+    /**
+     * <p>Returns whether <code>ch</code> is a value code, i.e.
+     * whether it represents a class in a pattern.</p>
+     * 
+     * @param ch the specified character
+     * @return true if <code>ch</code> is a value code, otherwise false.
+     */
+    public static boolean isValueCode(char ch) {
+        if( (ch != '@') &&
+            (ch != ':') &&
+            (ch != '%') &&
+            (ch != '+') &&
+            (ch != '#') &&
+            (ch != '<') &&
+            (ch != '>') &&
+            (ch != '*') &&
+            (ch != '/')
+          )
+        {
+            return false;
+        }
+        return true;
+    }       
+ 
+    /**
+     * <p>Returns the {@link Options} instance represented by 
+     * <code>pattern</code>.</p>
+     *
+     * @param pattern the pattern string
+     * @return The {@link Options} instance
+     */
+    public static Options parsePattern(String pattern) {
+        int sz = pattern.length();
+
+        char opt = ' ';
+        char ch = ' ';
+        boolean required = false;
+        Object type = null;
+
+        Options options = new Options();
+        
+        for(int i=0; i<sz; i++) {
+            ch = pattern.charAt(i);
+
+            // a value code comes after an option and specifies 
+            // details about it
+            if(!isValueCode(ch)) {
+                if(opt != ' ') {
+                    // we have a previous one to deal with
+                    options.addOption( OptionBuilder.hasArg( type != null )
+                                                    .isRequired( required )
+                                                    .withType( type )
+                                                    .create( opt ) );
+                    required = false;
+                    type = null;
+                    opt = ' ';
+                }
+                opt = ch;
+            } else
+            if(ch == '!') {
+                required = true;
+            } else {
+                type = getValueClass(ch);
+            }
+        }
+
+        if(opt != ' ') {
+            // we have a final one to deal with
+            options.addOption( OptionBuilder.hasArg( type != null )
+                                            .isRequired( required )
+                                            .withType( type )
+                                            .create( opt ) );
+        }
+
+        return options;
+    }
+
+}
diff --git a/installer/src/java/org/apache/commons/cli/PosixParser.java b/installer/src/java/org/apache/commons/cli/PosixParser.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/PosixParser.java
@@ -0,0 +1,342 @@
+/*
+ * $Header$
+ * $Revision: 2833 $
+ * $Date: 2006-06-28 22:51:26 -0700 (Wed, 28 Jun 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.commons.cli;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * The class PosixParser provides an implementation of the 
+ * {@link Parser#flatten(Options,String[],boolean) flatten} method.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @see Parser
+ * @version $Revision: 2833 $
+ */
+public class PosixParser extends Parser {
+
+    /** holder for flattened tokens */
+    private ArrayList tokens = new ArrayList();
+    /** specifies if bursting should continue */
+    private boolean eatTheRest;
+    /** holder for the current option */
+    private Option currentOption;
+    /** the command line Options */
+    private Options options;
+
+    /**
+     * <p>Resets the members to their original state i.e. remove
+     * all of <code>tokens</code> entries, set <code>eatTheRest</code>
+     * to false and set <code>currentOption</code> to null.</p>
+     */
+    private void init() {
+        eatTheRest = false;
+        tokens.clear();
+        currentOption = null;
+    }
+
+    /**
+     * <p>An implementation of {@link Parser}'s abstract
+     * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p>
+     *
+     * <p>The following are the rules used by this flatten method.
+     * <ol>
+     *  <li>if <code>stopAtNonOption</code> is <b>true</b> then do not
+     *  burst anymore of <code>arguments</code> entries, just add each
+     *  successive entry without further processing.  Otherwise, ignore
+     *  <code>stopAtNonOption</code>.</li>
+     *  <li>if the current <code>arguments</code> entry is "<b>--</b>"
+     *  just add the entry to the list of processed tokens</li>
+     *  <li>if the current <code>arguments</code> entry is "<b>-</b>"
+     *  just add the entry to the list of processed tokens</li>
+     *  <li>if the current <code>arguments</code> entry is two characters
+     *  in length and the first character is "<b>-</b>" then check if this
+     *  is a valid {@link Option} id.  If it is a valid id, then add the
+     *  entry to the list of processed tokens and set the current {@link Option}
+     *  member.  If it is not a valid id and <code>stopAtNonOption</code>
+     *  is true, then the remaining entries are copied to the list of 
+     *  processed tokens.  Otherwise, the current entry is ignored.</li>
+     *  <li>if the current <code>arguments</code> entry is more than two
+     *  characters in length and the first character is "<b>-</b>" then
+     *  we need to burst the entry to determine its constituents.  For more
+     *  information on the bursting algorithm see 
+     *  {@link PosixParser#burstToken( String, boolean) burstToken}.</li>
+     *  <li>if the current <code>arguments</code> entry is not handled 
+     *  by any of the previous rules, then the entry is added to the list
+     *  of processed tokens.</li>
+     * </ol>
+     * </p>
+     *
+     * @param options The command line {@link Options}
+     * @param arguments The command line arguments to be parsed
+     * @param stopAtNonOption Specifies whether to stop flattening
+     * when an non option is found.
+     * @return The flattened <code>arguments</code> String array.
+     */
+    protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption) {
+        init();
+        this.options = options;
+
+        // an iterator for the command line tokens
+        Iterator iter = Arrays.asList(arguments).iterator();
+        String token = null;
+
+        // process each command line token
+        while (iter.hasNext()) {
+
+            // get the next command line token
+            token = (String) iter.next();
+
+            // handle SPECIAL TOKEN
+            if (token.startsWith("--")) {
+                processLongOptionToken(token, stopAtNonOption);
+            } else if ("-".equals(token)) {
+                // single hyphen
+                processSingleHyphen(token);
+            } else if (token.startsWith("-")) {
+                int tokenLength = token.length();
+                if (tokenLength == 2) {
+                    processOptionToken(token, stopAtNonOption);
+                } else {
+                    boolean burst = true;
+                    if (token.length() > 2) {
+                        // check for misspelled long option
+                        String longOptionCandidate = "-" + token;
+                        if (this.options.hasOption(longOptionCandidate)) {
+                            tokens.add(token);
+                            burst = false;
+                        }
+                    }
+                    if (burst) {
+                        burstToken(token, stopAtNonOption);
+                    }
+                }
+            } else {
+                if (stopAtNonOption) {
+                    process(token);
+                } else {
+                    tokens.add(token);
+                }
+            }
+
+            gobble(iter);
+        }
+
+        return (String[]) tokens.toArray(new String[] {});
+    }
+
+    /**
+     * <p>Adds the remaining tokens to the processed tokens list.</p>
+     *
+     * @param iter An iterator over the remaining tokens
+     */
+    private void gobble( Iterator iter ) {
+        if( eatTheRest ) {
+            while( iter.hasNext() ) {
+                tokens.add( iter.next() );
+            }
+        }
+    }
+
+    /**
+     * <p>If there is a current option and it can have an argument
+     * value then add the token to the processed tokens list and 
+     * set the current option to null.</p>
+     * <p>If there is a current option and it can have argument
+     * values then add the token to the processed tokens list.</p>
+     * <p>If there is not a current option add the special token
+     * "<b>--</b>" and the current <code>value</code> to the processed
+     * tokens list.  The add all the remaining <code>argument</code>
+     * values to the processed tokens list.</p>
+     *
+     * @param value The current token
+     */
+    private void process( String value ) {
+        if( currentOption != null && currentOption.hasArg() ) {
+            if( currentOption.hasArg() ) {
+                tokens.add( value );
+                currentOption = null;
+            }
+            else if (currentOption.hasArgs() ) {
+                tokens.add( value );
+            }
+        }
+        else {
+            eatTheRest = true;
+            tokens.add( "--" );
+            tokens.add( value );
+        }
+    }
+
+    /**
+     * <p>If it is a hyphen then add the hyphen directly to
+     * the processed tokens list.</p>
+     *
+     * @param hyphen The hyphen token
+     */
+    private void processSingleHyphen( String hyphen ) {
+        tokens.add( hyphen );
+    }
+
+    /**
+     * <p>If an {@link Option} exists for <code>token</code> then
+     * set the current option and add the token to the processed 
+     * list.</p>
+     * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code>
+     * is set then ignore the current token and add the remaining tokens
+     * to the processed tokens list directly.</p>
+     *
+     * @param token The current option token
+     * @param stopAtNonOption Specifies whether flattening should halt
+     * at the first non option.
+     */
+    private void processOptionToken( String token, boolean stopAtNonOption ) {
+        if( this.options.hasOption( token ) ) {
+            currentOption = this.options.getOption( token );
+            tokens.add( token );
+        } else {
+            if( stopAtNonOption ) {
+                eatTheRest = true;
+            } else {
+                tokens.add(token);
+            }
+        }
+    }
+
+    /**
+     * same stop logic as for single hyphen options
+     * @param longToken
+     * @param stopAtNonOption
+     */
+    private void processLongOptionToken(String longToken, boolean stopAtNonOption ) {
+        String token;
+        String value = null;
+        if( longToken.indexOf( '=' ) != -1 ) {
+            token = longToken.substring( 0, longToken.indexOf( '=' ));
+            value = longToken.substring( longToken.indexOf( '=' ) + 1, longToken.length() );
+        } else {
+            token = longToken;
+        }   
+        if( this.options.hasOption(token)) {
+            tokens.add(token);
+            if( value != null) {
+                tokens.add(value);
+            }
+        } else {
+            if(stopAtNonOption) {
+                eatTheRest = true;
+            } else {
+                tokens.add(token);
+                if( value != null) {
+                    tokens.add(value);
+                }
+            }
+        }
+    }
+    
+    
+    /**
+     * <p>Breaks <code>token</code> into its constituent parts
+     * using the following algorithm.
+     * <ul>
+     *  <li>ignore the first character ("<b>-</b>" )</li>
+     *  <li>foreach remaining character check if an {@link Option}
+     *  exists with that id.</li>
+     *  <li>if an {@link Option} does exist then add that character
+     *  prepended with "<b>-</b>" to the list of processed tokens.</li>
+     *  <li>if the {@link Option} can have an argument value and there 
+     *  are remaining characters in the token then add the remaining 
+     *  characters as a token to the list of processed tokens.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b> 
+     *  <code>stopAtNonOption</code> <b>IS</b> set then add the special token
+     *  "<b>--</b>" followed by the remaining characters and also 
+     *  the remaining tokens directly to the processed tokens list.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
+     *  <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
+     *  character prepended with "<b>-</b>".</li>
+     * </ul>
+     * </p>
+     */
+    protected void burstToken( String token, boolean stopAtNonOption ) {
+        int tokenLength = token.length();
+
+        for( int i = 1; i < tokenLength; i++) {
+            String ch = String.valueOf( token.charAt( i ) );
+            boolean hasOption = options.hasOption( ch );
+
+            if( hasOption ) {
+                tokens.add( "-" + ch );
+                currentOption = options.getOption( ch );
+                if( currentOption.hasArg() && token.length()!=i+1 ) {
+                    tokens.add( token.substring( i+1 ) );
+                    break;
+                }
+            }
+            else if( stopAtNonOption ) {
+                process( token.substring( i ) );
+            }
+            else {
+                tokens.add( "-" + ch );
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/apache/commons/cli/TypeHandler.java b/installer/src/java/org/apache/commons/cli/TypeHandler.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/TypeHandler.java
@@ -0,0 +1,252 @@
+/*
+ * $Header$
+ * $Revision: 2664 $
+ * $Date: 2006-02-18 14:20:35 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.Date;
+
+/**
+  * This is a temporary implementation. TypeHandler will handle the 
+  * pluggableness of OptionTypes and it will direct all of these types 
+  * of conversion functionalities to ConvertUtils component in Commons 
+  * alreayd. BeanUtils I think.
+  *
+  * @author Henri Yandell (bayard @ generationjava.com)
+  * @version $Revision: 2664 $
+  */    
+public class TypeHandler {
+
+    /**
+     * <p>Returns the <code>Object</code> of type <code>obj</code>
+     * with the value of <code>str</code>.</p>
+     *
+     * @param str the command line value
+     * @param obj the type of argument
+     * @return The instance of <code>obj</code> initialised with
+     * the value of <code>str</code>.
+     */
+    public static Object createValue(String str, Object obj) {
+        return createValue(str, (Class)obj);
+    }
+
+    /**
+     * <p>Returns the <code>Object</code> of type <code>clazz</code>
+     * with the value of <code>str</code>.</p>
+     *
+     * @param str the command line value
+     * @param clazz the type of argument
+     * @return The instance of <code>clazz</code> initialised with
+     * the value of <code>str</code>.
+     */
+    public static Object createValue(String str, Class clazz) {
+        if( PatternOptionBuilder.STRING_VALUE == clazz) {
+            return str;
+        } else
+        if( PatternOptionBuilder.OBJECT_VALUE == clazz) {
+            return createObject(str);
+        } else
+        if( PatternOptionBuilder.NUMBER_VALUE == clazz) {
+            return createNumber(str);
+        } else
+        if( PatternOptionBuilder.DATE_VALUE   == clazz) {
+            return createDate(str);
+        } else
+        if( PatternOptionBuilder.CLASS_VALUE  == clazz) {
+            return createClass(str);
+        } else
+        if( PatternOptionBuilder.FILE_VALUE   == clazz) {
+            return createFile(str);
+        } else
+        if( PatternOptionBuilder.EXISTING_FILE_VALUE   == clazz) {
+            return createFile(str);
+        } else
+        if( PatternOptionBuilder.FILES_VALUE  == clazz) {
+            return createFiles(str);
+        } else
+        if( PatternOptionBuilder.URL_VALUE    == clazz) {
+            return createURL(str);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+      * <p>Create an Object from the classname and empty constructor.</p>
+      *
+      * @param str the argument value
+      * @return the initialised object, or null if it couldn't create the Object.
+      */
+    public static Object createObject(String str) {
+        Class cl = null;
+        try {
+            cl = Class.forName(str);
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("Unable to find: "+str);
+            return null;
+        }
+
+        Object instance = null;
+
+        try {
+            instance = cl.newInstance();
+        } catch (InstantiationException cnfe) {
+            System.err.println("InstantiationException; Unable to create: "+str);
+            return null;
+        }
+        catch (IllegalAccessException cnfe) {
+            System.err.println("IllegalAccessException; Unable to create: "+str);
+            return null;
+        }
+
+        return instance;
+    }
+
+    /**
+     * <p>Create a number from a String.</p>
+     *
+     * @param str the value
+     * @return the number represented by <code>str</code>, if <code>str</code>
+     * is not a number, null is returned.
+     */
+    public static Number createNumber(String str) {
+        // Needs to be able to create
+        try {
+            // do searching for decimal point etc, but atm just make an Integer
+            return new BigDecimal(str);
+        } catch (NumberFormatException nfe) {
+            System.err.println(nfe.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * <p>Returns the class whose name is <code>str</code>.</p>
+     *
+     * @param str the class name
+     * @return The class if it is found, otherwise return null
+     */
+    public static Class createClass(String str) {
+        try {
+            return Class.forName(str);
+        } catch (ClassNotFoundException cnfe) {
+            System.err.println("Unable to find: "+str);
+            return null;
+        }
+    }
+
+    /**
+     * <p>Returns the date represented by <code>str</code>.</p>
+     *
+     * @param str the date string
+     * @return The date if <code>str</code> is a valid date string,
+     * otherwise return null.
+     */
+    public static Date createDate(String str) {
+        Date date = null;
+        if(date == null) {
+            System.err.println("Unable to parse: "+str);
+        }
+        return date;
+    }
+
+    /**
+     * <p>Returns the URL represented by <code>str</code>.</p>
+     *
+     * @param str the URL string
+     * @return The URL is <code>str</code> is well-formed, otherwise
+     * return null.
+     */
+    public static URL createURL(String str) {
+        try {
+            return new URL(str);
+        } catch (MalformedURLException mue) {
+            System.err.println("Unable to parse: "+str);
+            return null;
+        }
+    }
+
+    /**
+     * <p>Returns the File represented by <code>str</code>.</p>
+     *
+     * @param str the File location
+     * @return The file represented by <code>str</code>.
+     */
+    public static File createFile(String str) {
+        return new File(str);
+    }
+
+    /**
+     * <p>Returns the File[] represented by <code>str</code>.</p>
+     *
+     * @param str the paths to the files
+     * @return The File[] represented by <code>str</code>.
+     */
+    public static File[] createFiles(String str) {
+// to implement/port:
+//        return FileW.findFiles(str);
+        return null;
+    }
+
+}
diff --git a/installer/src/java/org/apache/commons/cli/UnrecognizedOptionException.java b/installer/src/java/org/apache/commons/cli/UnrecognizedOptionException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/apache/commons/cli/UnrecognizedOptionException.java
@@ -0,0 +1,82 @@
+/*
+ * $Header$
+ * $Revision: 2662 $
+ * $Date: 2006-02-18 06:20:33 -0800 (Sat, 18 Feb 2006) $
+ *
+ * ====================================================================
+ *
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Commons", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache at apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.commons.cli;
+
+/** 
+ * <p>Exception thrown during parsing signalling an unrecognized
+ * option was seen.<p>
+ *
+ * @author bob mcwhiter (bob @ werken.com)
+ * @version $Revision: 2662 $
+ */
+public class UnrecognizedOptionException extends ParseException {
+    
+    /** 
+     * <p>Construct a new <code>UnrecognizedArgumentException</code> 
+     * with the specified detail message.</p>
+     *
+     * @param message the detail message
+     */
+    public UnrecognizedOptionException( String message ) {
+        super( message );
+    }
+}
diff --git a/installer/src/java/org/python/util/install/AbstractWizard.java b/installer/src/java/org/python/util/install/AbstractWizard.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/AbstractWizard.java
@@ -0,0 +1,438 @@
+package org.python.util.install;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.Cursor;
+import java.awt.Dialog;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.HeadlessException;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+import javax.swing.JSeparator;
+
+public abstract class AbstractWizard extends JDialog implements ValidationListener {
+
+    private class WizardGlassPane extends JPanel implements MouseListener, KeyListener {
+        WizardGlassPane() {
+            super();
+            setOpaque(false);
+            addMouseListener(this);
+            addKeyListener(this);
+        }
+
+        public void keyPressed(KeyEvent e) {
+        }
+
+        public void keyReleased(KeyEvent e) {
+        }
+
+        public void keyTyped(KeyEvent e) {
+        }
+
+        public void mouseClicked(MouseEvent e) {
+        }
+
+        public void mouseEntered(MouseEvent e) {
+        }
+
+        public void mouseExited(MouseEvent e) {
+        }
+
+        public void mousePressed(MouseEvent e) {
+        }
+
+        public void mouseReleased(MouseEvent e) {
+        }
+    }
+
+    private AbstractWizardPage _activePage = null;
+    private JPanel _buttonPanel;
+    private JSeparator _buttonSeparator;
+    private Action _cancelAction;
+    private JButton _cancelButton;
+    private CardLayout _cards;
+    private JPanel _content;
+    private WizardGlassPane _glassPane;
+    private AbstractWizardHeader _header;
+    private boolean _headerVisible = false;
+    private ArrayList _listeners;
+    private Action _nextAction;
+    private JButton _nextButton;
+    private ArrayList _pages;
+    private Action _previousAction;
+    private JButton _previousButton;
+
+    public AbstractWizard() {
+        super();
+        initWindow();
+        initActions();
+        initComponents();
+    }
+
+    public AbstractWizard(Dialog parent) throws HeadlessException {
+        super(parent);
+        initWindow();
+        initActions();
+        initComponents();
+    }
+
+    public AbstractWizard(Frame parent) throws HeadlessException {
+        super(parent);
+        initWindow();
+        initActions();
+        initComponents();
+    }
+
+    public final void addPage(AbstractWizardPage page) {
+        if (_pages == null)
+            _pages = new ArrayList();
+        if (page == null || _pages.contains(page))
+            return;
+        _pages.add(page);
+        page.setWizard(this);
+        int number = _pages.indexOf(page);
+        _content.add(page, "page" + number);
+    }
+
+    public final void addWizardListener(WizardListener listener) {
+        if (listener == null)
+            return;
+        if (_listeners == null)
+            _listeners = new ArrayList(5);
+        if (_listeners.contains(listener))
+            return;
+        _listeners.add(listener);
+    }
+
+    private void cancel() {
+        fireCancelEvent();
+        setVisible(false);
+    }
+
+    /**
+     * @return whether the wizard finished succesfully
+     */
+    protected abstract boolean finish();
+
+    private void fireCancelEvent() {
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        WizardEvent event = new WizardEvent(this);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            WizardListener listener = (WizardListener) it.next();
+            listener.wizardCancelled(event);
+        }
+    }
+
+    private void fireFinishedEvent() {
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        WizardEvent event = new WizardEvent(this);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            WizardListener listener = (WizardListener) it.next();
+            listener.wizardFinished(event);
+        }
+    }
+
+    private void fireNextEvent() {
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        WizardEvent event = new WizardEvent(this);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            WizardListener listener = (WizardListener) it.next();
+            listener.wizardNext(event);
+        }
+    }
+
+    private void firePreviousEvent() {
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        WizardEvent event = new WizardEvent(this);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            WizardListener listener = (WizardListener) it.next();
+            listener.wizardPrevious(event);
+        }
+    }
+
+    private void fireStartedEvent() {
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        WizardEvent event = new WizardEvent(this);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            WizardListener listener = (WizardListener) it.next();
+            listener.wizardStarted(event);
+        }
+    }
+
+    /**
+     * @return the String that will appear on the Cancel button
+     */
+    protected abstract String getCancelString();
+
+    /**
+     * @return the String that will appear on the Finish button
+     */
+    protected abstract String getFinishString();
+
+    /**
+     * @return the wizard header panel
+     */
+    public AbstractWizardHeader getHeader() {
+        return _header;
+    }
+
+    /**
+     * @return the String that will appear on the Next button
+     */
+    protected abstract String getNextString();
+
+    /**
+     * @return the String that will appear on the Previous button
+     */
+    protected abstract String getPreviousString();
+
+    /**
+     * usually only called from the WizardValidator, after validation succeeds
+     * 
+     * validation always occurs when the 'next' or 'finish' button was clicked, so when the validation succeeds, the
+     * wizard can go to the next page
+     */
+    public final void gotoNextPage() {
+        next();
+    }
+
+    private void initActions() {
+        _nextAction = new AbstractAction(getNextString()) {
+            public void actionPerformed(ActionEvent e) {
+                tryNext();
+            }
+        };
+        _previousAction = new AbstractAction(getPreviousString()) {
+            public void actionPerformed(ActionEvent e) {
+                previous();
+            }
+        };
+        _cancelAction = new AbstractAction(getCancelString()) {
+            public void actionPerformed(ActionEvent e) {
+                cancel();
+            }
+        };
+    }
+
+    private void initComponents() {
+        _pages = new ArrayList();
+        getContentPane().setLayout(new BorderLayout(0, 0));
+        _header = new WizardHeader();
+        getContentPane().add(_header, BorderLayout.NORTH);
+        _content = new JPanel();
+        _cards = new CardLayout();
+        _content.setLayout(_cards);
+
+        getContentPane().add(_content, BorderLayout.CENTER); // was: WEST
+
+        _buttonPanel = new JPanel();
+        _buttonSeparator = new JSeparator();
+        _cancelButton = new JButton();
+        _previousButton = new JButton();
+        _nextButton = new JButton();
+        _cancelButton.setAction(_cancelAction);
+        _previousButton.setAction(_previousAction);
+        _nextButton.setAction(_nextAction);
+        GridBagConstraints gridBagConstraints;
+        _buttonPanel.setLayout(new GridBagLayout());
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.gridwidth = 3;
+        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.insets = new Insets(1, 1, 1, 1);
+        gridBagConstraints.weightx = 1.0;
+        _buttonPanel.add(_buttonSeparator, gridBagConstraints);
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.insets = new Insets(5, 5, 5, 5);
+        gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.weighty = 1.0;
+        _buttonPanel.add(_cancelButton, gridBagConstraints);
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.insets = new Insets(5, 5, 5, 5);
+        gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+        _buttonPanel.add(_previousButton, gridBagConstraints);
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 2;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.insets = new Insets(5, 5, 5, 5);
+        gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+        _buttonPanel.add(_nextButton, gridBagConstraints);
+        getContentPane().add(_buttonPanel, BorderLayout.SOUTH);
+    }
+
+    private void initWindow() {
+        _glassPane = new WizardGlassPane();
+        setGlassPane(_glassPane);
+    }
+
+    /**
+     * @return whether the wizard header is visible
+     */
+    public final boolean isHeaderVisible() {
+        return _headerVisible;
+    }
+
+    /**
+     * lock the wizard dialog, preventing any user input
+     */
+    public final void lock() {
+        _glassPane.setVisible(true);
+        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+    }
+
+    private void next() {
+        if (_activePage == null)
+            return;
+        _activePage.passivate();
+        int activeIndex = _pages.indexOf(_activePage);
+        int nextIndex = activeIndex + 1;
+        if (nextIndex >= _pages.size()) {
+            tryFinish();
+            return;
+        } else {
+            _activePage = (AbstractWizardPage) _pages.get(nextIndex);
+            showActivePage();
+        }
+        fireNextEvent();
+    }
+
+    private void previous() {
+        if (_activePage == null)
+            return;
+        _activePage.passivate();
+        int activeIndex = _pages.indexOf(_activePage);
+        int previousIndex = activeIndex - 1;
+        if (previousIndex < 0)
+            return;
+        else {
+            _activePage = (AbstractWizardPage) _pages.get(previousIndex);
+            showActivePage();
+        }
+        firePreviousEvent();
+    }
+
+    public final void removeWizardListener(WizardListener listener) {
+        if (listener == null || _listeners == null || !_listeners.contains(listener))
+            return;
+        _listeners.remove(listener);
+    }
+
+    /**
+     * @param header the wizard header panel
+     */
+    public void setHeader(AbstractWizardHeader header) {
+        if (this._header != null) {
+            getContentPane().remove(header);
+        }
+        this._header = header;
+        if (this._header != null) {
+            getContentPane().add(header, BorderLayout.NORTH);
+        }
+    }
+
+    /**
+     * @param visible show the header in this wizard?
+     */
+    public final void setHeaderVisible(boolean visible) {
+        _headerVisible = visible;
+        if (_header != null)
+            _header.setVisible(visible);
+    }
+
+    @Override
+    public void setVisible(boolean visible) {
+		if (visible) {
+			fireStartedEvent();
+			if (_pages.size() > 0) {
+				_activePage = (AbstractWizardPage) _pages.get(0);
+				showActivePage();
+			}
+		}
+		super.setVisible(visible);
+	}
+
+    private void showActivePage() {
+        if (_activePage == null)
+            return;
+        int number = _pages.indexOf(_activePage);
+        // show the active page
+        _cards.show(_content, "page" + number);
+        // update the wizard header
+        if (_header != null) {
+            _header.setTitle(_activePage.getTitle());
+            _header.setDescription(_activePage.getDescription());
+            _header.setIcon(_activePage.getIcon());
+        }
+        // set visibility and localized text of buttons
+        if (number == 0) {
+            _previousButton.setVisible(false);
+        } else {
+            _previousButton.setVisible(_activePage.isPreviousVisible());
+        }
+        _previousAction.putValue(Action.NAME, getPreviousString());
+        _cancelButton.setVisible(_activePage.isCancelVisible());
+        _cancelAction.putValue(Action.NAME, getCancelString());
+        _nextButton.setVisible(_activePage.isNextVisible());
+        _nextAction.putValue(Action.NAME, getNextString());
+        if (number + 1 == _pages.size()) {
+            _nextAction.putValue(Action.NAME, getFinishString());
+        } else {
+            _nextAction.putValue(Action.NAME, getNextString());
+        }
+        if (_nextButton.isVisible()) {
+            getRootPane().setDefaultButton(_nextButton);
+            // workaround wrong default button (e.g. on OverviewPage)
+            if (_activePage.getFocusField() == null) {
+                _nextButton.grabFocus();
+            }
+        }
+        _activePage.doActivate();
+    }
+
+    private void tryFinish() {
+        if (finish()) {
+            this.setVisible(false);
+            fireFinishedEvent();
+        }
+    }
+
+    private void tryNext() {
+        if (_activePage == null)
+            return;
+        _activePage.validateInput();
+    }
+
+    /**
+     * unlock the wizard dialog, allowing user input
+     */
+    public final void unlock() {
+        _glassPane.setVisible(false);
+        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/AbstractWizardHeader.java b/installer/src/java/org/python/util/install/AbstractWizardHeader.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/AbstractWizardHeader.java
@@ -0,0 +1,12 @@
+package org.python.util.install;
+
+import javax.swing.ImageIcon;
+import javax.swing.JPanel;
+
+abstract class AbstractWizardHeader extends JPanel {
+    protected abstract void setTitle(String title);
+
+    protected abstract void setDescription(String description);
+
+    protected abstract void setIcon(ImageIcon icon);
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/AbstractWizardPage.java b/installer/src/java/org/python/util/install/AbstractWizardPage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/AbstractWizardPage.java
@@ -0,0 +1,153 @@
+package org.python.util.install;
+
+import java.awt.GridBagConstraints;
+import java.awt.Insets;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+
+public abstract class AbstractWizardPage extends JPanel implements TextKeys {
+    private static final long serialVersionUID = -5233805023557214279L;
+
+    private static final String _ICON_FILE_NAME = "jython_small_c.png";
+
+    private static ImageIcon _imageIcon = null;
+    private AbstractWizardValidator _validator = null;
+    private AbstractWizard _wizard;
+
+    public AbstractWizardPage() {
+        super();
+        setValidator(null);
+    }
+
+    /**
+     * This method is called when the wizard page is activated, after Wizard.next();
+     */
+    protected abstract void activate();
+
+    /**
+     * This method is called right before validation of the page
+     */
+    protected abstract void beforeValidate();
+
+    /**
+     * called from Wizard, right after this page is set visible
+     */
+    final void doActivate() {
+        if (getFocusField() != null)
+            this.getFocusField().grabFocus();
+        activate();
+    }
+
+    /**
+     * @return the description of this page, which will be displayed in the wizard header
+     */
+    protected abstract String getDescription();
+
+    /**
+     * @return the input field on the page that should grab the focus when the page is activated
+     */
+    protected abstract JComponent getFocusField();
+
+    /**
+     * @return the icon that should be displayed in the header in all steps
+     */
+    protected ImageIcon getIcon() {
+        if (_imageIcon == null) {
+            URL iconURL = FileHelper.getRelativeURL(getClass(), _ICON_FILE_NAME);
+            if (iconURL != null) {
+                _imageIcon = new ImageIcon(iconURL);
+            }
+        }
+        return _imageIcon;
+    }
+
+    /**
+     * @return the title of this page, which will be displayed in the wizard header
+     */
+    protected abstract String getTitle();
+
+    /**
+     * @return the wizard this page belongs to
+     */
+    public final AbstractWizard getWizard() {
+        return _wizard;
+    }
+
+    /**
+     * @return whether the <i>cancel </i> button is visible
+     */
+    protected abstract boolean isCancelVisible();
+
+    /**
+     * @return whether the <i>next </i> button is visible
+     */
+    protected abstract boolean isNextVisible();
+
+    /**
+     * @return whether the <i>previous </i> button is visible
+     */
+    protected abstract boolean isPreviousVisible();
+
+    /**
+     * this method is called right before the page is hidden, but after the validation
+     */
+    protected abstract void passivate();
+
+    /**
+     * Set the validator for this page. The validator is called when the <i>next </i> button is clicked.
+     * 
+     * @param validator the validator for this page. If this is null, a EmptyValidator is assigned
+     */
+    public final void setValidator(AbstractWizardValidator validator) {
+        if (validator == null)
+            this._validator = new EmptyValidator();
+        else
+            this._validator = validator;
+        this._validator.setWizardPage(this);
+        this._validator.addValidationListener(_wizard);
+    }
+
+    /**
+     * @param wizard the wizard this page belongs to
+     */
+    final void setWizard(AbstractWizard wizard) {
+        this._wizard = wizard;
+        _validator.addValidationListener(wizard);
+    }
+
+    /**
+     * perform the validation of this page, using the assigned WizardValidator
+     */
+    final void validateInput() {
+        beforeValidate();
+        if (_validator == null)
+            return;
+        _validator.start();
+    }
+
+    /**
+     * @return default grid bag constraints
+     */
+    protected GridBagConstraints newGridBagConstraints() {
+        GridBagConstraints gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.anchor = GridBagConstraints.WEST;
+        gridBagConstraints.insets = new Insets(5, 5, 5, 5);
+        return gridBagConstraints;
+    }
+
+    final String getText(String textKey) {
+        return Installation.getText(textKey);
+    }
+
+    final String getText(String textKey, String parameter0) {
+        return Installation.getText(textKey, parameter0);
+    }
+
+    final String getText(String textKey, String parameter0, String parameter1) {
+        return Installation.getText(textKey, parameter0, parameter1);
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/AbstractWizardValidator.java b/installer/src/java/org/python/util/install/AbstractWizardValidator.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/AbstractWizardValidator.java
@@ -0,0 +1,132 @@
+package org.python.util.install;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public abstract class AbstractWizardValidator implements TextKeys {
+
+    /**
+     * The thread that performs the validation
+     */
+    private class ValidatorThread extends Thread {
+        public final void run() {
+            try {
+                fireValidationStarted();
+                validate();
+                fireValidationSucceeded();
+            } catch (ValidationException e) {
+                fireValidationFailed(e);
+            } catch (ValidationInformationException vie) {
+                fireValidationInformationRequired(vie);
+            }
+        }
+    }
+
+    private ArrayList _listeners;
+    private AbstractWizardPage _page;
+    private Thread _validatorThread;
+
+    public final void addValidationListener(ValidationListener listener) {
+        if (listener == null)
+            return;
+        if (_listeners == null)
+            _listeners = new ArrayList(5);
+        if (_listeners.contains(listener))
+            return;
+        _listeners.add(listener);
+    }
+
+    private void fireValidationFailed(ValidationException exception) {
+        if (getWizard() != null)
+            getWizard().unlock();
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        ValidationEvent event = new ValidationEvent(_page);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            ValidationListener listener = (ValidationListener) it.next();
+            listener.validationFailed(event, exception);
+        }
+    }
+
+    private void fireValidationInformationRequired(ValidationInformationException exception) {
+        if (getWizard() != null)
+            getWizard().unlock();
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        ValidationEvent event = new ValidationEvent(_page);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            ValidationListener listener = (ValidationListener) it.next();
+            listener.validationInformationRequired(event, exception);
+        }
+    }
+
+    private void fireValidationStarted() {
+        if (getWizard() != null)
+            getWizard().lock();
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        ValidationEvent event = new ValidationEvent(_page);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            ValidationListener listener = (ValidationListener) it.next();
+            listener.validationStarted(event);
+        }
+    }
+
+    private void fireValidationSucceeded() {
+        if (getWizard() != null) {
+            getWizard().unlock();
+            getWizard().gotoNextPage();
+        }
+        if (_listeners == null || _listeners.isEmpty())
+            return;
+        ValidationEvent event = new ValidationEvent(_page);
+        for (Iterator it = _listeners.iterator(); it.hasNext();) {
+            ValidationListener listener = (ValidationListener) it.next();
+            listener.validationSucceeded(event);
+        }
+    }
+
+    protected final AbstractWizard getWizard() {
+        if (_page != null) {
+            return _page.getWizard();
+        } else {
+            return null;
+        }
+    }
+
+    protected final AbstractWizardPage getWizardPage() {
+        return _page;
+    }
+
+    public final void removeValidationListener(ValidationListener listener) {
+        if (listener == null || _listeners == null || !_listeners.contains(listener))
+            return;
+        _listeners.remove(listener);
+    }
+
+    public final void setWizardPage(AbstractWizardPage page) {
+        this._page = page;
+    }
+
+    public final void start() {
+        _validatorThread = new ValidatorThread();
+        _validatorThread.start();
+    }
+
+    /**
+     * perform the actual validation
+     * 
+     * @throws ValidationException when the validation failed
+     * @throws ValidationInformationException when an information should be displayed
+     */
+    protected abstract void validate() throws ValidationException, ValidationInformationException;
+
+    final String getText(String textKey) {
+        return Installation.getText(textKey);
+    }
+
+    final String getText(String textKey, String parameter0) {
+        return Installation.getText(textKey, parameter0);
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ChildProcess.java b/installer/src/java/org/python/util/install/ChildProcess.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ChildProcess.java
@@ -0,0 +1,357 @@
+package org.python.util.install;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * Easy start of a child process.
+ * <p>
+ * Features are:
+ * <ul>
+ * <li>wait for the child process to finish.
+ * <li>kill the child process after a specified timeout.
+ * <li>get the output of the child process (System.out and System.err) redirected to the calling
+ * process, unless in silent mode.
+ * </ul>
+ */
+public class ChildProcess {
+
+    /**
+     * Inner class for reading stdout of the child process and printing it onto the caller's stdout.
+     */
+    private class StdoutMonitor extends Thread {
+
+        private StdoutMonitor() {}
+
+        public void run() {
+            String line = null;
+            BufferedReader stdout = new BufferedReader(new InputStreamReader(_process.getInputStream()));
+            try {
+                // blocks until input found or process dead
+                while ((line = stdout.readLine()) != null) {
+                    if (!isSilent()) {
+                        System.out.println(line);
+                    }
+                }
+            } catch (IOException ioe) {
+                if (!isSilent()) {
+                    ioe.printStackTrace();
+                }
+            } finally {
+                if (stdout != null)
+                    try {
+                        stdout.close();
+                    } catch (IOException e) {}
+            }
+        }
+    }
+
+    /**
+     * Inner class for reading stderr of the child process and printing it onto the caller's stderr.
+     */
+    private class StderrMonitor extends Thread {
+
+        private StderrMonitor() {}
+
+        public void run() {
+            String line = null;
+            BufferedReader stderr = new BufferedReader(new InputStreamReader(_process.getErrorStream()));
+            try {
+                // blocks until input found or process dead
+                while ((line = stderr.readLine()) != null) {
+                    if (!isSilent()) {
+                        System.err.println(line);
+                    }
+                }
+            } catch (IOException ioe) {
+                if (!isSilent()) {
+                    ioe.printStackTrace();
+                }
+            } finally {
+                if (stderr != null)
+                    try {
+                        stderr.close();
+                    } catch (IOException e) {}
+            }
+        }
+    }
+
+    /**
+     * Constant indicating no timeout at all.
+     */
+    public static final long INFINITE_TIMEOUT = -1;
+
+    /**
+     * Constant indicating the exit value if the child process was destroyed due to a timeout.
+     */
+    public static final int DESTROYED_AFTER_TIMEOUT = -9898;
+
+    /**
+     * Constant indicating that the exit value was not yet set
+     */
+    private static final int NOT_SET_EXITVALUE = -9;
+
+    /**
+     * The command as an array of strings
+     */
+    private String _command[] = null;
+
+    /**
+     * The timeout (in milliseconds)
+     */
+    private long _timeout = INFINITE_TIMEOUT;
+
+    /**
+     * The interval for checking if the child process is still alive
+     */
+    private long _pollAliveInterval = 1000;
+
+    /**
+     * the effective child process
+     */
+    Process _process;
+
+    /**
+     * the exit value
+     */
+    private int _exitValue = NOT_SET_EXITVALUE;
+
+    /**
+     * The start time of the child process
+     */
+    private long _startTime;
+
+    /**
+     * debug option (default is false)
+     */
+    private boolean _debug = false;
+
+    /**
+     * silent flag
+     */
+    private boolean _silent = false;
+
+    /**
+     * Default constructor
+     */
+    public ChildProcess() {}
+
+    /**
+     * Constructor taking a command array as an argument
+     * 
+     * @param command
+     *            The command to be executed, every token as array element.
+     */
+    public ChildProcess(String command[]) {
+        setCommand(command);
+    }
+
+    /**
+     * Constructor taking a command array and the timeout as an argument
+     * 
+     * @param command
+     *            The command to be executed, every token as array element.
+     * @param timeout
+     *            in milliseconds. Special value: <code>INFINITE_TIMEOUT</code> indicates no timeout
+     *            at all.
+     */
+    public ChildProcess(String command[], long timeout) {
+        setCommand(command);
+        setTimeout(timeout);
+    }
+
+    /**
+     * Set the command array. This will override (but not overwrite) a previously set command
+     */
+    public void setCommand(String command[]) {
+        _command = command;
+    }
+
+    /**
+     * Returns the command array
+     */
+    public String[] getCommand() {
+        return _command;
+    }
+
+    /**
+     * Set the timeout (how long should the calling process wait for the child).
+     * 
+     * @param timeout
+     *            in milliseconds. Special value: <code>INFINITE_TIMEOUT</code> indicates no timeout
+     *            at all. This is the default.
+     */
+    public void setTimeout(long timeout) {
+        _timeout = timeout;
+    }
+
+    /**
+     * Returns the timeout in milliseconds.
+     */
+    public long getTimeout() {
+        return _timeout;
+    }
+
+    /**
+     * Set the debug flag.
+     * <p>
+     * Setting this to true will print the submitted command and an information if the child process
+     * is destroyed after the timeout.
+     */
+    public void setDebug(boolean debug) {
+        _debug = debug;
+    }
+
+    /**
+     * Returns the debug flag
+     */
+    public boolean isDebug() {
+        return _debug;
+    }
+
+    /**
+     * Set the silent flag.
+     * <p>
+     * Setting this to true will suppress output of the called command.
+     */
+    public void setSilent(boolean silent) {
+        _silent = silent;
+    }
+
+    /**
+     * Returns the silent flag.
+     */
+    public boolean isSilent() {
+        return _silent;
+    }
+
+    /**
+     * Set the interval (in milliseconds) after which the subprocess is checked if it is still
+     * alive. Defaults to 1000 ms.
+     */
+    public void setPollAliveInterval(long pollAliveInterval) {
+        _pollAliveInterval = pollAliveInterval;
+    }
+
+    /**
+     * Returns the interval (in milliseconds) after which the subprocess is checked if it is still
+     * alive.
+     */
+    public long getPollAliveInterval() {
+        return _pollAliveInterval;
+    }
+
+    /**
+     * returns true if the timeout has expired
+     */
+    private boolean isTimeout() {
+        boolean isTimeout = false;
+        long currentTime = System.currentTimeMillis();
+        long diff = 0;
+        long timeout = getTimeout();
+        if (timeout != INFINITE_TIMEOUT) {
+            diff = currentTime - _startTime;
+            if (diff > timeout) {
+                isTimeout = true;
+            }
+        }
+        return isTimeout;
+    }
+
+    /**
+     * Start the child process
+     */
+    public int run() {
+        try {
+            // determine start time
+            _startTime = System.currentTimeMillis();
+            // start the process
+            _process = Runtime.getRuntime().exec(getCommand());
+            debugCommand();
+            // handle stdout and stderr
+            StdoutMonitor stdoutMonitor = new StdoutMonitor();
+            stdoutMonitor.start();
+            StderrMonitor stderrMonitor = new StderrMonitor();
+            stderrMonitor.start();
+            // run the subprocess as long as wanted
+            while (!isTimeout() && isAlive()) {
+                try {
+                    Thread.sleep(getPollAliveInterval());
+                } catch (InterruptedException ie) {
+                    if (!isSilent()) {
+                        ie.printStackTrace();
+                    }
+                }
+            }
+            // end properly
+            if (isAlive()) { // sets the exit value in case process is dead
+                destroy();
+            } else {
+                if (isDebug()) {
+                    System.out.println("[ChildProcess] ended itself");
+                }
+            }
+        } catch (IOException ioe) {
+            if (!isSilent()) {
+                ioe.printStackTrace();
+            }
+        }
+        return getExitValue();
+    }
+
+    /**
+     * The exit value
+     */
+    public int getExitValue() {
+        return _exitValue;
+    }
+
+    private void setExitValue(int exitValue) {
+        _exitValue = exitValue;
+    }
+
+    /**
+     * Tests if the process is still alive
+     */
+    private boolean isAlive() {
+        try {
+            setExitValue(_process.exitValue());
+            return false;
+        } catch (IllegalThreadStateException itse) {
+            return true;
+        }
+    }
+
+    /**
+     * Destroy the child process
+     */
+    private void destroy() {
+        _process.destroy();
+        setExitValue(DESTROYED_AFTER_TIMEOUT);
+        if (isDebug()) {
+            System.out.println("[ChildProcess] destroying because of timeout !");
+        }
+    }
+
+    /**
+     * Lists the submitted command (if so indicated)
+     */
+    private void debugCommand() {
+        if (isDebug()) {
+            String[] command = getCommand();
+            if (command != null) {
+                System.out.print("[ChildProcess] command '");
+                for (int i = 0; i < command.length; i++) {
+                    String commandPart = command[i];
+                    if (i == 0) {
+                        System.out.print(commandPart);
+                    } else {
+                        System.out.print(" " + commandPart);
+                    }
+                }
+                System.out.println("' is now running...");
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ConsoleInstaller.java b/installer/src/java/org/python/util/install/ConsoleInstaller.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ConsoleInstaller.java
@@ -0,0 +1,609 @@
+package org.python.util.install;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import org.python.util.install.Installation.JavaVersionInfo;
+import org.python.util.install.driver.Tunnel;
+
+public class ConsoleInstaller implements ProgressListener, TextKeys {
+
+    public static final String CURRENT_JRE = "=";
+
+    private static final String _CANCEL = "c";
+
+    private static final String _PROMPT = ">>>";
+
+    private static final String _BEGIN_ANSWERS = "[";
+
+    private static final String _END_ANSWERS = "]";
+
+
+    private InstallerCommandLine _commandLine;
+
+    private JarInstaller _jarInstaller;
+
+    private JarInfo _jarInfo;
+
+    private Tunnel _tunnel;
+
+    public ConsoleInstaller(InstallerCommandLine commandLine, JarInfo jarInfo) {
+        _commandLine = commandLine;
+        _jarInfo = jarInfo;
+        _jarInstaller = new JarInstaller(this, jarInfo);
+    }
+
+    public void setTunnel(Tunnel tunnel) {
+        _tunnel = tunnel;
+    }
+
+    public void install() {
+        File targetDirectory = null;
+        JavaHomeHandler javaHomeHandler = null;
+        if (_commandLine.hasConsoleOption()) {
+            welcome();
+            selectLanguage();
+            acceptLicense();
+            InstallationType installationType = selectInstallationType();
+            targetDirectory = determineTargetDirectory();
+            javaHomeHandler = checkVersion(determineJavaHome());
+            promptForCopying(targetDirectory, installationType, javaHomeHandler);
+            _jarInstaller.inflate(targetDirectory, installationType, javaHomeHandler);
+            showReadme(targetDirectory);
+            success(targetDirectory);
+        } else if (_commandLine.hasSilentOption()) {
+            message(getText(C_SILENT_INSTALLATION));
+            targetDirectory = _commandLine.getTargetDirectory();
+            checkTargetDirectorySilent(targetDirectory);
+            javaHomeHandler = checkVersionSilent(_commandLine.getJavaHomeHandler());
+            _jarInstaller.inflate(targetDirectory,
+                                  _commandLine.getInstallationType(),
+                                  javaHomeHandler);
+            success(targetDirectory);
+        }
+    }
+
+    protected final static void message(String message) {
+        System.out.println(message); // this System.out.println is intended
+    }
+
+    private void welcome() {
+        message(getText(C_WELCOME_TO_JYTHON));
+        message(getText(C_VERSION_INFO, _jarInfo.getVersion()));
+        message(getText(C_AT_ANY_TIME_CANCEL, _CANCEL));
+    }
+
+    private String question(String question) {
+        return question(question, null, false, null);
+    }
+
+    private String question(String question, boolean answerRequired) {
+        return question(question, null, answerRequired, null);
+    }
+
+    private String question(String question, List<String> answers, String defaultAnswer) {
+        return question(question, answers, true, defaultAnswer);
+    }
+
+    /**
+     * question and answer
+     * 
+     * @param question
+     * @param answers
+     *            Possible answers (may be null)
+     * @param answerRequired
+     * @param defaultAnswer
+     *            (may be null)
+     * 
+     * @return (chosen) answer
+     */
+    private String question(String question,
+                            List<String> answers,
+                            boolean answerRequired,
+                            String defaultAnswer) {
+        try {
+            if (answers != null && answers.size() > 0) {
+                question = question + " " + _BEGIN_ANSWERS;
+                Iterator<String> answersAsIterator = answers.iterator();
+                while (answersAsIterator.hasNext()) {
+                    if (!question.endsWith(_BEGIN_ANSWERS))
+                        question = question + "/";
+                    String possibleAnswer = answersAsIterator.next();
+                    if (possibleAnswer.equalsIgnoreCase(defaultAnswer)) {
+                        if (Character.isDigit(possibleAnswer.charAt(0))) {
+                            question = question.concat(" ").concat(possibleAnswer).concat(" ");
+                        } else {
+                            question = question + possibleAnswer.toUpperCase();
+                        }
+                    } else {
+                        question = question + possibleAnswer;
+                    }
+                }
+                question = question + _END_ANSWERS;
+            }
+            question = question + " " + _PROMPT + " ";
+            boolean match = false;
+            String answer = "";
+            while (!match && !_CANCEL.equalsIgnoreCase(answer)) {
+                // output to normal System.out
+                System.out.print(question); // intended print, not println (!)
+                answer = readLine();
+                if ("".equals(answer) && answerRequired) {
+                    // check default answer
+                    if (defaultAnswer != null) {
+                        match = true;
+                        answer = defaultAnswer;
+                    }
+                } else {
+                    if (answers != null && answers.size() > 0) {
+                        Iterator<String> answersAsIterator = answers.iterator();
+                        while (answersAsIterator.hasNext()) {
+                            if (answer.equalsIgnoreCase(answersAsIterator.next())) {
+                                match = true;
+                            }
+                        }
+                    } else {
+                        match = true;
+                    }
+                    if (!match && !_CANCEL.equalsIgnoreCase(answer)) {
+                        message(getText(C_INVALID_ANSWER, answer));
+                    }
+                }
+            }
+            if (_CANCEL.equalsIgnoreCase(answer)) {
+                throw new InstallationCancelledException();
+            }
+            return answer;
+        } catch (IOException ioe) {
+            throw new InstallerException(ioe);
+        }
+    }
+
+    /**
+     * Send a signal through the tunnel, and then wait for the answer from the other side.
+     * 
+     * <pre>
+     *            (2)  [Driver]   receives question  [Tunnel]   sends question   [Console]  (1)
+     *            (3)  [Driver]   sends answer       [Tunnel]   receives answer  [Console]  (4)
+     * </pre>
+     */
+    private String readLine() throws IOException {
+        InputStream inputStream;
+        String line = "";
+        if (_tunnel == null) {
+            inputStream = System.in;
+        } else {
+            inputStream = _tunnel.getAnswerReceiverStream();
+            _tunnel.getQuestionSenderStream().write(Tunnel.NEW_LINE.getBytes());
+            _tunnel.getQuestionSenderStream().flush();
+        }
+        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+        line = reader.readLine();
+        return line;
+    }
+
+    private void selectLanguage() {
+        List<String> availableLanguages = new ArrayList<String>(2);
+        availableLanguages.add(getText(C_ENGLISH));
+        availableLanguages.add(getText(C_GERMAN)); // 1 == German
+        List<String> answers = new ArrayList<String>(availableLanguages.size());
+        String languages = "";
+        String defaultAnswer = null;
+        for (Iterator<String> iterator = availableLanguages.iterator(); iterator.hasNext();) {
+            String language = iterator.next();
+            String possibleAnswer = language.substring(0, 1);
+            if (defaultAnswer == null) {
+                defaultAnswer = possibleAnswer;
+            }
+            languages = languages + language + ", ";
+            answers.add(possibleAnswer.toLowerCase());
+        }
+        languages = languages.substring(0, languages.length() - 2);
+        message(getText(C_AVAILABLE_LANGUAGES, languages));
+        String answer = question(getText(C_SELECT_LANGUAGE), answers, defaultAnswer);
+        if (answer.equalsIgnoreCase(answers.get(1))) {
+            Installation.setLanguage(Locale.GERMAN);
+        } else {
+            Installation.setLanguage(Locale.ENGLISH);
+        }
+    }
+
+    private InstallationType selectInstallationType() {
+        InstallationType installationType = new InstallationType();
+        String no = getText(C_NO);
+        String yes = getText(C_YES);
+        message(getText(C_INSTALL_TYPES));
+        message("  " + Installation.ALL + ". " + getText(C_ALL));
+        message("  " + Installation.STANDARD + ". " + getText(C_STANDARD));
+        message("  " + Installation.MINIMUM + ". " + getText(C_MINIMUM));
+        message("  " + Installation.STANDALONE + ". " + getText(C_STANDALONE));
+        String answer = question(getText(C_SELECT_INSTALL_TYPE), getTypeAnswers(), Installation.ALL);
+        if (Installation.ALL.equals(answer)) {
+            installationType.setAll();
+        } else if (Installation.STANDARD.equals(answer)) {
+            installationType.setStandard();
+        } else if (Installation.MINIMUM.equals(answer)) {
+            installationType.setMinimum();
+        } else if (Installation.STANDALONE.equals(answer)) {
+            installationType.setStandalone();
+        }
+        if (!installationType.isStandalone()) {
+            // include parts ?
+            if (!installationType.isAll()) {
+                answer = question(getText(C_INCLUDE), getYNAnswers(), no);
+                if (yes.equals(answer)) {
+                    do {
+                        answer = question(getText(C_INEXCLUDE_PARTS, no), getInExcludeAnswers(), no);
+                        if (InstallerCommandLine.INEXCLUDE_LIBRARY_MODULES.equals(answer)) {
+                            installationType.addLibraryModules();
+                        } else if (InstallerCommandLine.INEXCLUDE_DEMOS_AND_EXAMPLES.equals(answer)) {
+                            installationType.addDemosAndExamples();
+                        } else if (InstallerCommandLine.INEXCLUDE_DOCUMENTATION.equals(answer)) {
+                            installationType.addDocumentation();
+                        } else if (InstallerCommandLine.INEXCLUDE_SOURCES.equals(answer)) {
+                            installationType.addSources();
+                        }
+                        if (!no.equals(answer)) {
+                            message(getText(C_SCHEDULED, answer));
+                        }
+                    } while (!no.equals(answer));
+                }
+            }
+            // exclude parts ?
+            if (!installationType.isMinimum()) {
+                answer = question(getText(C_EXCLUDE), getYNAnswers(), no);
+                if (yes.equals(answer)) {
+                    do {
+                        answer = question(getText(C_INEXCLUDE_PARTS, no), getInExcludeAnswers(), no);
+                        if (InstallerCommandLine.INEXCLUDE_LIBRARY_MODULES.equals(answer)) {
+                            installationType.removeLibraryModules();
+                        } else if (InstallerCommandLine.INEXCLUDE_DEMOS_AND_EXAMPLES.equals(answer)) {
+                            installationType.removeDemosAndExamples();
+                        } else if (InstallerCommandLine.INEXCLUDE_DOCUMENTATION.equals(answer)) {
+                            installationType.removeDocumentation();
+                        } else if (InstallerCommandLine.INEXCLUDE_SOURCES.equals(answer)) {
+                            installationType.removeSources();
+                        }
+                        if (!no.equals(answer)) {
+                            message(getText(C_UNSCHEDULED, answer));
+                        }
+                    } while (!no.equals(answer));
+                }
+            }
+        }
+        return installationType;
+    }
+
+    private JavaHomeHandler checkVersion(JavaHomeHandler javaHomeHandler) {
+        // handle target java version
+        JavaInfo javaInfo = verifyTargetJava(javaHomeHandler);
+        message(getText(C_JAVA_VERSION,
+                        javaInfo.getJavaVersionInfo().getVendor(),
+                        javaInfo.getJavaVersionInfo().getVersion()));
+        if (!Installation.isValidJava(javaInfo.getJavaVersionInfo())) {
+            message(getText(C_UNSUPPORTED_JAVA));
+            question(getText(C_PROCEED_ANYWAY));
+        }
+        // handle OS
+        String osName = System.getProperty(Installation.OS_NAME);
+        String osVersion = System.getProperty(Installation.OS_VERSION);
+        message(getText(C_OS_VERSION, osName, osVersion));
+        if (!Installation.isValidOs()) {
+            message(getText(C_UNSUPPORTED_OS));
+            question(getText(C_PROCEED_ANYWAY));
+        }
+        return javaInfo.getJavaHomeHandler();
+    }
+
+    private JavaHomeHandler checkVersionSilent(JavaHomeHandler javaHomeHandler) {
+        // check target java version
+        JavaInfo javaInfo = verifyTargetJava(javaHomeHandler);
+        if (!Installation.isValidJava(javaInfo.getJavaVersionInfo())) {
+            message(getText(C_UNSUPPORTED_JAVA));
+        }
+        // check OS
+        if (!Installation.isValidOs()) {
+            message(getText(C_UNSUPPORTED_OS));
+        }
+        return javaInfo.getJavaHomeHandler();
+    }
+
+    private JavaInfo verifyTargetJava(JavaHomeHandler javaHomeHandler) {
+        JavaVersionInfo javaVersionInfo = new JavaVersionInfo();
+        Installation.fillJavaVersionInfo(javaVersionInfo, System.getProperties()); // a priori
+        if (javaHomeHandler.isDeviation()) {
+            javaVersionInfo = Installation.getExternalJavaVersion(javaHomeHandler);
+            if (javaVersionInfo.getErrorCode() != Installation.NORMAL_RETURN) {
+                // switch back to current if an error occurred
+                message(getText(C_TO_CURRENT_JAVA, javaVersionInfo.getReason()));
+                javaHomeHandler = new JavaHomeHandler();
+            }
+        }
+        JavaInfo javaInfo = new JavaInfo();
+        javaInfo.setJavaHomeHandler(javaHomeHandler);
+        javaInfo.setJavaVersionInfo(javaVersionInfo);
+        return javaInfo;
+    }
+
+    private void acceptLicense() {
+        String no = getText(C_NO);
+        String yes = getText(C_YES);
+        String read = question(getText(C_READ_LICENSE), getYNAnswers(), no);
+        if (read.equalsIgnoreCase(getText(C_YES))) {
+            String licenseText = "n/a";
+            try {
+                licenseText = _jarInfo.getLicenseText();
+                message(licenseText);
+            } catch (IOException ioe) {
+                throw new InstallerException(ioe);
+            }
+        }
+        String accept = question(getText(C_ACCEPT), getYNAnswers(), yes);
+        if (!accept.equalsIgnoreCase(getText(C_YES))) {
+            throw new InstallationCancelledException();
+        }
+    }
+
+    private File determineTargetDirectory() {
+        String no = getText(C_NO);
+        String yes = getText(C_YES);
+        File targetDirectory = null;
+        try {
+            do {
+                targetDirectory = new File(question(getText(C_ENTER_TARGET_DIRECTORY), true));
+                if (targetDirectory.exists()) {
+                    if (!targetDirectory.isDirectory()) {
+                        message(getText(C_NOT_A_DIRECTORY, targetDirectory.getCanonicalPath()));
+                    } else {
+                        if (targetDirectory.list().length > 0) {
+                            String overwrite = question(getText(C_OVERWRITE_DIRECTORY,
+                                                                targetDirectory.getCanonicalPath()),
+                                                        getYNAnswers(),
+                                                        no);
+                            if (overwrite.equalsIgnoreCase(getText(C_YES))) {
+                                String clear = question(getText(C_CLEAR_DIRECTORY,
+                                                                targetDirectory.getCanonicalPath()),
+                                                        getYNAnswers(),
+                                                        yes);
+                                if (clear.equalsIgnoreCase(getText(C_YES))) {
+                                    clearDirectory(targetDirectory);
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    String create = question(getText(C_CREATE_DIRECTORY,
+                                                     targetDirectory.getCanonicalPath()),
+                                             getYNAnswers(),
+                                             yes);
+                    if (create.equalsIgnoreCase(getText(C_YES))) {
+                        if (!targetDirectory.mkdirs()) {
+                            throw new InstallerException(getText(C_UNABLE_CREATE_DIRECTORY,
+                                                                 targetDirectory.getCanonicalPath()));
+                        }
+                    }
+                }
+            } while (!targetDirectory.exists() || !targetDirectory.isDirectory()
+                    || targetDirectory.list().length > 0);
+        } catch (IOException ioe) {
+            throw new InstallerException(ioe);
+        }
+        return targetDirectory;
+    }
+
+    private JavaHomeHandler determineJavaHome() {
+        JavaHomeHandler javaHomeHandler = null;
+        boolean javaFound = false;
+        while (!javaFound) {
+            String javaHomeName = question(getText(C_ENTER_JAVA_HOME), null, true, CURRENT_JRE);
+            // only validate deviations
+            if (CURRENT_JRE.equals(javaHomeName)) {
+                javaHomeHandler = new JavaHomeHandler();
+                javaFound = true;
+            } else {
+                javaHomeHandler = new JavaHomeHandler(javaHomeName);
+                if (javaHomeHandler.isDeviation()) {
+                    if (!javaHomeHandler.isValidHome()) {
+                        String binDirName = javaHomeName.concat("/bin");
+                        message(getText(C_NO_JAVA_EXECUTABLE, binDirName));
+                    } else {
+                        javaFound = true;
+                    }
+                } else {
+                    javaFound = true;
+                }
+            }
+        }
+        return javaHomeHandler;
+    }
+
+    private void checkTargetDirectorySilent(File targetDirectory) {
+        try {
+            if (!targetDirectory.exists()) {
+                // create directory
+                if (!targetDirectory.mkdirs()) {
+                    throw new InstallerException(getText(C_UNABLE_CREATE_DIRECTORY,
+                                                         targetDirectory.getCanonicalPath()));
+                }
+            } else {
+                // assert it is an empty directory
+                if (!targetDirectory.isDirectory()) {
+                    throw new InstallerException(getText(C_NOT_A_DIRECTORY,
+                                                         targetDirectory.getCanonicalPath()));
+                } else {
+                    if (targetDirectory.list().length > 0) {
+                        throw new InstallerException(getText(C_NON_EMPTY_TARGET_DIRECTORY,
+                                                             targetDirectory.getCanonicalPath()));
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            throw new InstallerException(ioe);
+        }
+    }
+
+    private void showReadme(final File targetDirectory) {
+        String no = getText(C_NO);
+        String read = question(getText(C_READ_README), getYNAnswers(), no);
+        if (read.equalsIgnoreCase(getText(C_YES))) {
+            try {
+                message(_jarInfo.getReadmeText());
+                question(getText(C_PROCEED));
+            } catch (IOException ioe) {
+                throw new InstallerException(ioe);
+            }
+        }
+    }
+
+    private void clearDirectory(File targetDirectory) {
+        File files[] = targetDirectory.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].isDirectory()) {
+                clearDirectory(files[i]);
+            }
+            if (!files[i].delete()) {
+                throw new InstallerException(getText(C_UNABLE_TO_DELETE, files[i].getAbsolutePath()));
+            }
+        }
+    }
+
+    private void promptForCopying(final File targetDirectory,
+                                  final InstallationType installationType,
+                                  final JavaHomeHandler javaHomeHandler) {
+        try {
+            message(getText(C_SUMMARY));
+            if (installationType.isStandalone()) {
+                message(" - " + InstallerCommandLine.TYPE_STANDALONE);
+            } else {
+                message("  - " + InstallerCommandLine.INEXCLUDE_LIBRARY_MODULES + ": "
+                        + installationType.installLibraryModules());
+                message("  - " + InstallerCommandLine.INEXCLUDE_DEMOS_AND_EXAMPLES + ": "
+                        + installationType.installDemosAndExamples());
+                message("  - " + InstallerCommandLine.INEXCLUDE_DOCUMENTATION + ": "
+                        + installationType.installDocumentation());
+                message("  - " + InstallerCommandLine.INEXCLUDE_SOURCES + ": "
+                        + installationType.installSources());
+                if (javaHomeHandler.isValidHome()) {
+                    message("  - JRE: " + javaHomeHandler.getHome().getAbsolutePath());
+                } else {
+                    message("  - java");
+                }
+            }
+            String proceed = question(getText(C_CONFIRM_TARGET, targetDirectory.getCanonicalPath()),
+                                      getYNAnswers(), getText(C_YES));
+            if (!proceed.equalsIgnoreCase(getText(C_YES))) {
+                throw new InstallationCancelledException();
+            }
+        } catch (IOException ioe) {
+            throw new InstallerException(ioe); // catch for the compiler
+        }
+    }
+
+    private void success(final File targetDirectory) {
+        try {
+            message(getText(C_CONGRATULATIONS) + " "
+                    + getText(C_SUCCESS, _jarInfo.getVersion(), targetDirectory.getCanonicalPath()));
+        } catch (IOException ioe) {
+            throw new InstallerException(ioe); // catch for the compiler
+        }
+    }
+
+    private List<String> getTypeAnswers() {
+        List<String> answers = new ArrayList<String>(4);
+        answers.add(Installation.ALL);
+        answers.add(Installation.STANDARD);
+        answers.add(Installation.MINIMUM);
+        answers.add(Installation.STANDALONE);
+        return answers;
+    }
+
+    private List<String> getYNAnswers() {
+        List<String> answers = new ArrayList<String>(2);
+        answers.add(getText(C_YES));
+        answers.add(getText(C_NO));
+        return answers;
+    }
+
+    private List<String> getInExcludeAnswers() {
+        List<String> answers = new ArrayList<String>(5);
+        answers.add(InstallerCommandLine.INEXCLUDE_LIBRARY_MODULES);
+        answers.add(InstallerCommandLine.INEXCLUDE_DEMOS_AND_EXAMPLES);
+        answers.add(InstallerCommandLine.INEXCLUDE_DOCUMENTATION);
+        answers.add(InstallerCommandLine.INEXCLUDE_SOURCES);
+        answers.add(getText(C_NO));
+        return answers;
+    }
+
+    private void progressMessage(int percentage) {
+        message(" " + percentage + " %");
+    }
+
+    private String getText(String textKey) {
+        return Installation.getText(textKey);
+    }
+
+    private String getText(String textKey, String parameter0) {
+        return Installation.getText(textKey, parameter0);
+    }
+
+    private String getText(String textKey, String parameter0, String parameter1) {
+        return Installation.getText(textKey, parameter0, parameter1);
+    }
+
+    private static class JavaInfo {
+
+        private JavaVersionInfo _javaVersionInfo;
+
+        private JavaHomeHandler _javaHomeHandler;
+
+        void setJavaHomeHandler(JavaHomeHandler javaHomeHandler) {
+            _javaHomeHandler = javaHomeHandler;
+        }
+
+        JavaHomeHandler getJavaHomeHandler() {
+            return _javaHomeHandler;
+        }
+
+        void setJavaVersionInfo(JavaVersionInfo javaVersionInfo) {
+            _javaVersionInfo = javaVersionInfo;
+        }
+
+        JavaVersionInfo getJavaVersionInfo() {
+            return _javaVersionInfo;
+        }
+    }
+
+    //
+    // interface ProgressListener
+    //
+    public void progressChanged(int newPercentage) {
+        progressMessage(newPercentage);
+    }
+
+    public int getInterval() {
+        return 10; // fixed interval for console installer
+    }
+
+    public void progressFinished() {
+        progressMessage(100);
+    }
+
+    public void progressEntry(String entry) {
+    // ignore the single entries - only used in gui mode
+    }
+
+    public void progressStartScripts() {
+        message(getText(C_GENERATING_START_SCRIPTS));
+    }
+
+    public void progressStandalone() {
+        message(getText(C_PACKING_STANDALONE_JAR));
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/DirectoryFilter.java b/installer/src/java/org/python/util/install/DirectoryFilter.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/DirectoryFilter.java
@@ -0,0 +1,23 @@
+package org.python.util.install;
+
+import java.io.File;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * Filters all directories, and sets the description in the file chooser
+ */
+public class DirectoryFilter extends FileFilter {
+
+    public boolean accept(File f) {
+        if (f.isDirectory()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public String getDescription() {
+        return Installation.getText(TextKeys.DIRECTORIES_ONLY);
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/DirectorySelectionPage.java b/installer/src/java/org/python/util/install/DirectorySelectionPage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/DirectorySelectionPage.java
@@ -0,0 +1,200 @@
+package org.python.util.install;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.io.File;
+import java.io.IOException;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+public class DirectorySelectionPage extends AbstractWizardPage {
+
+    private static final long serialVersionUID = -3672273150338356549L;
+    
+    private JLabel _label;
+    private JTextField _directory;
+    private JButton _browse;
+    private JarInfo _jarInfo;
+
+    public DirectorySelectionPage(JarInfo jarInfo) {
+        super();
+        _jarInfo = jarInfo;
+        initComponents();
+    }
+
+    private void initComponents() {
+        // label
+        _label = new JLabel();
+        // directory
+        _directory = new JTextField(40);
+        _directory.addFocusListener(new DirectoryFocusListener());
+        // browse button
+        _browse = new JButton();
+        _browse.addActionListener(new BrowseButtonListener());
+
+        JPanel panel = new JPanel();
+        GridBagLayout gridBagLayout = new GridBagLayout();
+        panel.setLayout(gridBagLayout);
+        GridBagConstraints gridBagConstraints = newGridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        panel.add(_label, gridBagConstraints);
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        panel.add(_directory, gridBagConstraints);
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 1;
+        panel.add(_browse, gridBagConstraints);
+
+        add(panel);
+    }
+
+    JTextField getDirectory() {
+        return _directory;
+    }
+
+    protected String getTitle() {
+        return getText(TARGET_DIRECTORY_PROPERTY);
+    }
+
+    protected String getDescription() {
+        return getText(CHOOSE_LOCATION);
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return true;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        return _directory;
+    }
+
+    protected void activate() {
+        _label.setText(getText(SELECT_TARGET_DIRECTORY) + ": ");
+        _browse.setText(getText(BROWSE));
+        String directory = FrameInstaller.getTargetDirectory();
+        if (directory == null || directory.length() <= 0) {
+            File defaultDirectory = getDefaultDirectory();
+            try {
+                directory = defaultDirectory.getCanonicalPath();
+            } catch (IOException e) {
+                directory = "?";
+            }
+            FrameInstaller.setTargetDirectory(directory);
+        }
+        _directory.setText(FrameInstaller.getTargetDirectory());
+        _directory.setToolTipText(_directory.getText());
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+    private File getDefaultDirectory() {
+        String directory = "";
+        File defaultDirectory = null;
+        // 1st try (on windows): root
+        if (Installation.isWindows()) {
+            JavaHomeHandler handler = new JavaHomeHandler();
+            if (handler.isValidHome()) {
+                directory = handler.getHome().getAbsolutePath();
+                if (directory.length() > 2) {
+                    directory = directory.substring(0, 2);
+                }
+            } else {
+                directory = "C:";
+            }
+            defaultDirectory = makeJythonSubDirectory(directory);
+        }
+        // 2st try: user.home
+        if (defaultDirectory == null) {
+            directory = System.getProperty("user.home", "");
+            if (directory.length() > 0) {
+                defaultDirectory = makeJythonSubDirectory(directory);
+            }
+        }
+        // 3rd try: user.dir
+        if (defaultDirectory == null) {
+            directory = System.getProperty("user.dir", "");
+            if (directory.length() > 0) {
+                defaultDirectory = makeJythonSubDirectory(directory);
+            }
+        }
+        // 4th try: current directory
+        if (defaultDirectory == null) {
+            defaultDirectory = makeJythonSubDirectory(new File(new File("dummy").getAbsolutePath()).getParent());
+        }
+        return defaultDirectory;
+    }
+
+    private File makeJythonSubDirectory(String directory) {
+        File defaultDirectory = null;
+        File parentDirectory = new File(directory);
+        if (parentDirectory.exists() && parentDirectory.isDirectory()) {
+            String jythonSubDirectoryName = "jython" + (_jarInfo.getVersion()).replaceAll("\\+", "");
+            defaultDirectory = new File(parentDirectory, jythonSubDirectoryName);
+        }
+        return defaultDirectory;
+    }
+
+    private class BrowseButtonListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+            String directoryName = _directory.getText();
+            File directory = new File(directoryName);
+            if (directory.exists()) {
+                if (!directory.isDirectory()) {
+                    // switch to parent directory if user typed the name of a file
+                    directory = directory.getParentFile();
+                }
+            }
+            JFileChooser fileChooser = new JFileChooser(directory);
+            fileChooser.setDialogTitle(getText(SELECT_TARGET_DIRECTORY));
+            // the filter is at the moment only used for the title of the dialog:
+            fileChooser.setFileFilter(new DirectoryFilter());
+            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+            if (fileChooser.isAcceptAllFileFilterUsed()) {
+                if (Installation.isMacintosh() && Installation.isJDK141()) {
+                    // work around ArrayIndexOutOfBoundsExceptio on Mac OS X, java version 1.4.1
+                } else {
+                    fileChooser.setAcceptAllFileFilterUsed(false);
+                }
+            }
+            int returnValue = fileChooser.showDialog(_browse, getText(SELECT));
+            if (returnValue == JFileChooser.APPROVE_OPTION) {
+                _directory.setText(fileChooser.getSelectedFile().getAbsolutePath());
+                _directory.setToolTipText(_directory.getText());
+                FrameInstaller.setTargetDirectory(_directory.getText());
+            }
+        }
+    }
+
+    private class DirectoryFocusListener implements FocusListener {
+        public void focusGained(FocusEvent e) {
+        }
+
+        public void focusLost(FocusEvent e) {
+            FrameInstaller.setTargetDirectory(_directory.getText());
+            _directory.setToolTipText(_directory.getText());
+        }
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/DirectorySelectionPageValidator.java b/installer/src/java/org/python/util/install/DirectorySelectionPageValidator.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/DirectorySelectionPageValidator.java
@@ -0,0 +1,36 @@
+package org.python.util.install;
+
+import java.io.File;
+
+public class DirectorySelectionPageValidator extends AbstractWizardValidator {
+
+    DirectorySelectionPage _page;
+
+    DirectorySelectionPageValidator(DirectorySelectionPage page) {
+        super();
+        _page = page;
+    }
+
+    protected void validate() throws ValidationException, ValidationInformationException {
+        String directory = _page.getDirectory().getText().trim(); // trim to be sure
+        if (directory != null && directory.length() > 0) {
+            File targetDirectory = new File(directory);
+            if (targetDirectory.exists()) {
+                if (targetDirectory.isDirectory()) {
+                    if (targetDirectory.list().length > 0) {
+                        throw new ValidationException(getText(NON_EMPTY_TARGET_DIRECTORY));
+                    }
+                }
+            } else {
+                if (targetDirectory.mkdirs()) {
+                    throw new ValidationInformationException(Installation.getText(CREATED_DIRECTORY, directory));
+                } else {
+                    throw new ValidationException(getText(UNABLE_CREATE_DIRECTORY, directory));
+                }
+            }
+        } else {
+            throw new ValidationException(getText(EMPTY_TARGET_DIRECTORY));
+        }
+        FrameInstaller.setTargetDirectory(directory);
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/EmptyValidator.java b/installer/src/java/org/python/util/install/EmptyValidator.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/EmptyValidator.java
@@ -0,0 +1,8 @@
+package org.python.util.install;
+
+public class EmptyValidator extends AbstractWizardValidator {
+
+    protected void validate() {
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/FileHelper.java b/installer/src/java/org/python/util/install/FileHelper.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/FileHelper.java
@@ -0,0 +1,208 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Helper methods for file handling during installation / installation verification
+ */
+public final class FileHelper {
+
+    private final static String EXECUTABLE_MODE = "755";
+
+    /**
+     * create a temporary directory with the same name as the passed in File (which may exist as
+     * file, not directory)
+     * 
+     * @param tempDirectory
+     * @return <code>true</code> only if the the directory was successfully created (or already
+     *         existed)
+     */
+    public static boolean createTempDirectory(File tempDirectory) {
+        boolean success = true;
+        if (!tempDirectory.isDirectory()) {
+            if (tempDirectory.exists()) {
+                success = carryOnResult(tempDirectory.delete(), success);
+            }
+            if (success) {
+                success = tempDirectory.mkdirs();
+            }
+        }
+        return success;
+    }
+
+    /**
+     * completely remove a directory
+     * 
+     * @param dir
+     * @return <code>true</code> if successful, <code>false</code> otherwise.
+     */
+    public static boolean rmdir(File dir) {
+        boolean success = true;
+        if (dir.exists()) {
+            File[] files = dir.listFiles();
+            for (int i = 0; i < files.length; i++) {
+                File file = files[i];
+                if (file.isFile()) {
+                    success = carryOnResult(file.delete(), success);
+                } else {
+                    if (file.isDirectory()) {
+                        success = carryOnResult(rmdir(file), success);
+                    }
+                }
+            }
+            success = carryOnResult(dir.delete(), success);
+        }
+        return success;
+    }
+
+    /**
+     * read the contents of a file into a String
+     * 
+     * @param file
+     *            The file has to exist
+     * @return The contents of the file as String
+     * @throws IOException
+     */
+    public static String readAll(File file) throws IOException {
+        FileReader fileReader = new FileReader(file);
+        try {
+            StringBuffer sb = new StringBuffer();
+            char[] b = new char[8192];
+            int n;
+            while ((n = fileReader.read(b)) > 0) {
+                sb.append(b, 0, n);
+            }
+            return sb.toString();
+        } finally {
+            fileReader.close();
+        }
+    }
+
+    /**
+     * read the contents of a stream into a String
+     * <p>
+     * ATTN: does not handle encodings
+     * 
+     * @param inputStream
+     *            The input stream
+     * @return A String representation of the file contents
+     * @throws IOException
+     */
+    public static String readAll(InputStream inputStream) throws IOException {
+        try {
+            StringBuffer sb = new StringBuffer();
+            byte[] b = new byte[8192];
+            int n;
+            while ((n = inputStream.read(b)) > 0) {
+                sb.append(new String(b, 0, n));
+            }
+            return sb.toString();
+        } finally {
+            inputStream.close();
+        }
+    }
+
+    /**
+     * Write contents to a file.
+     * <p>
+     * An existing file would be overwritten.
+     * 
+     * @param file
+     * @param contents
+     * 
+     * @throws IOException
+     */
+    public static void write(File file, String contents) throws IOException {
+        FileWriter writer = new FileWriter(file);
+        writer.write(contents);
+        writer.flush();
+        writer.close();
+    }
+
+    /**
+     * determine the url of a file relative to (in the same directory as) the specified .class file<br>
+     * can also be used if the .class file resides inside a .jar file
+     * 
+     * @param clazz
+     *            The class next to the file
+     * @param fileName
+     *            The name of the file
+     * 
+     * @return The url of the file, can be null
+     */
+    public static URL getRelativeURL(Class<?> clazz, String fileName) {
+        String filePath = getRelativePackagePath(clazz) + "/" + fileName;
+        return Thread.currentThread().getContextClassLoader().getResource(filePath);
+    }
+
+    /**
+     * get the input stream of a file relative to (in the same directory as) the specified .class
+     * file<br>
+     * can also be used if the .class file resides inside a .jar file
+     * 
+     * @param clazz
+     *            The class next to the file
+     * @param fileName
+     *            The name of the file
+     * 
+     * @return The input stream of the file, can be null
+     */
+    public static InputStream getRelativeURLAsStream(Class<?> clazz, String fileName) {
+        String filePath = getRelativePackagePath(clazz) + "/" + fileName;
+        return Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
+    }
+
+    /**
+     * do a chmod on the passed file
+     * 
+     * @param scriptFile
+     */
+    public static void makeExecutable(File scriptFile) {
+        try {
+            String command[] = new String[3];
+            command[0] = "chmod";
+            command[1] = EXECUTABLE_MODE;
+            command[2] = scriptFile.getAbsolutePath();
+            long timeout = 3000;
+            ChildProcess childProcess = new ChildProcess(command, timeout);
+            childProcess.run();
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    /**
+     * build the package path for the class loader<br>
+     * the class loader should be able to load a file appended to this path, if it is in the same
+     * directory.
+     * 
+     * @param clazz
+     *            The class
+     * 
+     * @return The package path
+     */
+    private static String getRelativePackagePath(Class<?> clazz) {
+        String className = clazz.getName();
+        String packageName = className.substring(0, className.lastIndexOf("."));
+        return packageName.replace('.', '/');
+    }
+
+    /**
+     * @param newResult
+     * @param existingResult
+     * @return <code>false</code> if newResult or existingResult are false, <code>true</code>
+     *         otherwise.
+     */
+    private static boolean carryOnResult(boolean newResult, boolean existingResult) {
+        if (existingResult) {
+            return newResult;
+        } else {
+            return existingResult;
+        }
+    }
+}
diff --git a/installer/src/java/org/python/util/install/FrameInstaller.java b/installer/src/java/org/python/util/install/FrameInstaller.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/FrameInstaller.java
@@ -0,0 +1,186 @@
+package org.python.util.install;
+
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.Locale;
+import java.util.Properties;
+
+import javax.swing.UIManager;
+
+import org.python.util.install.Installation.JavaVersionInfo;
+import org.python.util.install.driver.Autotest;
+
+public class FrameInstaller {
+    private static final String TRUE = "1";
+    private static final String FALSE = "0";
+
+    private static final String JAVA_VERSION_PROPERTY = "FrameInstaller.Version";
+    private static final String JAVA_VENDOR_PROPERTY = "FrameInstaller.Vendor";
+    private static final String JAVA_SPEC_VERSION_PROPERTY = "FrameInstaller.SpecVersion";
+
+    private static final String INEX_MOD_PROPERTY = "FrameInstaller.mod";
+    private static final String INEX_DEMO_PROPERTY = "FrameInstaller.demo";
+    private static final String INEX_DOC_PROPERTY = "FrameInstaller.doc";
+    private static final String INEX_SRC_PROPERTY = "FrameInstaller.src";
+    private static final String STANDALONE_PROPERTY = "FrameInstaller.standalone";
+
+    private static Properties _properties = new Properties();
+
+    private static JavaHomeHandler _javaHomeHandler;
+
+    protected FrameInstaller(InstallerCommandLine commandLine, JarInfo jarInfo, Autotest autotest) {
+        try {
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        } catch (Exception e) {
+        }
+        // clear all properties
+        _properties.clear();
+        // set the default for the target directory
+        if (commandLine.hasDirectoryOption()) {
+            setTargetDirectory(commandLine.getTargetDirectory().getAbsolutePath());
+        }
+        if (commandLine.hasJavaHomeOption()) {
+            setJavaHomeHandler(commandLine.getJavaHomeHandler());
+        }
+        initDefaultJava();
+        Wizard wizard = new Wizard(jarInfo, autotest);
+        wizard.addWindowListener(new WindowAdapter() {
+            public void windowClosing(WindowEvent event) {
+                if (!Installation.isAutotesting()) {
+                    System.exit(0);
+                }
+            }
+        });
+        wizard.addWizardListener(new SimpleWizardListener());
+        wizard.setVisible(true);
+    }
+
+    protected static void setProperty(String key, String value) {
+        _properties.setProperty(key, value);
+    }
+
+    protected static String getProperty(String key) {
+        return _properties.getProperty(key);
+    }
+
+    protected static String getProperty(String key, String defaultValue) {
+        return _properties.getProperty(key, defaultValue);
+    }
+
+    protected static void setTargetDirectory(String targetDirectory) {
+        setProperty(TextKeys.TARGET_DIRECTORY_PROPERTY, targetDirectory.trim());
+    }
+
+    protected static String getTargetDirectory() {
+        return getProperty(TextKeys.TARGET_DIRECTORY_PROPERTY);
+    }
+
+    protected static void setJavaHomeHandler(JavaHomeHandler javaHomeHandler) {
+        _javaHomeHandler = javaHomeHandler;
+    }
+
+    protected static JavaHomeHandler getJavaHomeHandler() {
+        if (_javaHomeHandler == null) {
+            _javaHomeHandler = new JavaHomeHandler();
+        }
+        return _javaHomeHandler;
+    }
+
+    protected static void setLanguage(Locale locale) {
+        setProperty(TextKeys.LANGUAGE_PROPERTY, locale.toString());
+        Installation.setLanguage(locale);
+    }
+
+    protected static Locale getLanguage() {
+        return new Locale(getProperty(TextKeys.LANGUAGE_PROPERTY));
+    }
+
+    protected static InstallationType getInstallationType() {
+        InstallationType installationType = new InstallationType();
+        if (Boolean.valueOf(getProperty(STANDALONE_PROPERTY)).booleanValue()) {
+            installationType.setStandalone();
+        }
+        if (Boolean.valueOf(getProperty(INEX_MOD_PROPERTY)).booleanValue()) {
+            installationType.addLibraryModules();
+        } else {
+            installationType.removeLibraryModules();
+        }
+        if (Boolean.valueOf(getProperty(INEX_DEMO_PROPERTY)).booleanValue()) {
+            installationType.addDemosAndExamples();
+        } else {
+            installationType.removeDemosAndExamples();
+        }
+        if (Boolean.valueOf(getProperty(INEX_DOC_PROPERTY)).booleanValue()) {
+            installationType.addDocumentation();
+        } else {
+            installationType.removeDocumentation();
+        }
+        if (Boolean.valueOf(getProperty(INEX_SRC_PROPERTY)).booleanValue()) {
+            installationType.addSources();
+        } else {
+            installationType.removeSources();
+        }
+        return installationType;
+    }
+
+    protected static void setInstallationType(InstallationType installationType) {
+        setProperty(STANDALONE_PROPERTY, Boolean.toString(installationType.isStandalone()));
+        setProperty(INEX_MOD_PROPERTY, Boolean.toString(installationType.installLibraryModules()));
+        setProperty(INEX_DEMO_PROPERTY, Boolean.toString(installationType.installDemosAndExamples()));
+        setProperty(INEX_DOC_PROPERTY, Boolean.toString(installationType.installDocumentation()));
+        setProperty(INEX_SRC_PROPERTY, Boolean.toString(installationType.installSources()));
+    }
+
+    protected static JavaVersionInfo getJavaVersionInfo() {
+        JavaVersionInfo javaVersionInfo = new JavaVersionInfo();
+        javaVersionInfo.setVersion(getProperty(JAVA_VERSION_PROPERTY));
+        javaVersionInfo.setVendor(getProperty(JAVA_VENDOR_PROPERTY));
+        javaVersionInfo.setSpecificationVersion(getProperty(JAVA_SPEC_VERSION_PROPERTY));
+        return javaVersionInfo;
+    }
+
+    protected static void setJavaVersionInfo(JavaVersionInfo javaVersionInfo) {
+        setProperty(JAVA_VERSION_PROPERTY, javaVersionInfo.getVersion());
+        setProperty(JAVA_VENDOR_PROPERTY, javaVersionInfo.getVendor());
+        setProperty(JAVA_SPEC_VERSION_PROPERTY, javaVersionInfo.getSpecificationVersion());
+    }
+
+    protected static void setAccept(boolean accept) {
+        if (accept) {
+            setProperty(TextKeys.ACCEPT_PROPERTY, TRUE);
+        } else {
+            setProperty(TextKeys.ACCEPT_PROPERTY, FALSE);
+        }
+    }
+
+    protected static boolean isAccept() {
+        return TRUE.equals(getProperty(TextKeys.ACCEPT_PROPERTY, FALSE));
+    }
+
+    protected static void initDefaultJava() {
+        JavaVersionInfo javaVersionInfo = new JavaVersionInfo();
+        Installation.fillJavaVersionInfo(javaVersionInfo, System.getProperties());
+        FrameInstaller.setJavaVersionInfo(javaVersionInfo);
+    }
+
+    private class SimpleWizardListener implements WizardListener {
+        public void wizardStarted(WizardEvent event) {
+        }
+
+        public void wizardFinished(WizardEvent event) {
+            if (!Installation.isAutotesting()) {
+                System.exit(0);
+            }
+        }
+
+        public void wizardCancelled(WizardEvent event) {
+            System.exit(1);
+        }
+
+        public void wizardNext(WizardEvent event) {
+        }
+
+        public void wizardPrevious(WizardEvent event) {
+        }
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/Installation.java b/installer/src/java/org/python/util/install/Installation.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/Installation.java
@@ -0,0 +1,439 @@
+package org.python.util.install;
+
+import java.awt.GraphicsEnvironment; // should be allowed on headless systems
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.StringTokenizer;
+
+import org.python.util.install.driver.Autotest;
+import org.python.util.install.driver.InstallationDriver;
+import org.python.util.install.driver.Tunnel;
+
+public class Installation {
+    public final static int NORMAL_RETURN = 0;
+    public final static int ERROR_RETURN = 1;
+    
+    protected static final String ALL = "1";
+    protected static final String STANDARD = "2";
+    protected static final String MINIMUM = "3";
+    protected static final String STANDALONE = "9";
+
+    protected static final String OS_NAME = "os.name";
+    protected static final String OS_VERSION = "os.version";
+    protected static final String JAVA_VM_NAME = "java.vm.name";
+    protected static final String EMPTY = "";
+
+    protected static final String HEADLESS_PROPERTY_NAME = "java.awt.headless";
+
+    private static final String RESOURCE_CLASS = "org.python.util.install.TextConstants";
+
+    private static ResourceBundle _textConstants = ResourceBundle.getBundle(RESOURCE_CLASS, Locale.getDefault());
+
+    private static boolean _verbose = false;
+    private static boolean _isAutotesting = false;
+
+    public static void main(String args[]) {
+        internalMain(args, null, null);
+    }
+
+    public static void driverMain(String args[], Autotest autotest, Tunnel tunnel) {
+        internalMain(args, autotest, tunnel);
+    }
+
+    protected static boolean isVerbose() {
+        return _verbose;
+    }
+    
+    protected static void setVerbose(boolean verbose) {
+        _verbose = verbose;
+    }
+
+    protected static boolean isAutotesting() {
+        return _isAutotesting;
+    }
+
+    protected static String getText(String key) {
+        return _textConstants.getString(key);
+    }
+
+    protected static String getText(String key, String... parameters) {
+        return MessageFormat.format(_textConstants.getString(key), (Object[])parameters);
+    }
+
+    protected static void setLanguage(Locale locale) {
+        _textConstants = ResourceBundle.getBundle(RESOURCE_CLASS, locale);
+    }
+
+    public static boolean isValidOs() {
+        String osName = System.getProperty(OS_NAME, "");
+        String lowerOs = osName.toLowerCase();
+        if (isWindows()) {
+            return true;
+        }
+        if (lowerOs.indexOf("linux") >= 0) {
+            return true;
+        }
+        if (lowerOs.indexOf("mac") >= 0) {
+            return true;
+        }
+        if (lowerOs.indexOf("unix") >= 0) {
+            return true;
+        }
+        return false;
+    }
+
+    protected static boolean isValidJava(JavaVersionInfo javaVersionInfo) {
+        String specificationVersion = javaVersionInfo.getSpecificationVersion();
+        verboseOutput("specification version: '" + specificationVersion + "'");
+        boolean valid = true;
+        if (getJavaSpecificationVersion(specificationVersion) < 15) {
+            valid = false;
+        }
+        return valid;
+    }
+
+    /**
+     * @return specification version as an int, e.g. 15 or 16 (the micro part is ignored)
+     * @param specificationVersion
+     *            as system property
+     */
+    public static int getJavaSpecificationVersion(String specificationVersion) {
+        // major.minor.micro
+        // according to http://java.sun.com/j2se/1.5.0/docs/guide/versioning/spec/versioning2.html
+        String major = "1";
+        String minor = "0";
+        StringTokenizer tokenizer = new StringTokenizer(specificationVersion, ".");
+        if (tokenizer.hasMoreTokens()) {
+            major = tokenizer.nextToken();
+        }
+        if (tokenizer.hasMoreTokens()) {
+            minor = tokenizer.nextToken();
+        }
+        return Integer.valueOf(major.concat(minor)).intValue();
+    }
+
+    public static boolean isWindows() {
+        boolean isWindows = false;
+        String osName = System.getProperty(OS_NAME, "");
+        if (osName.toLowerCase().indexOf("windows") >= 0) {
+            isWindows = true;
+        }
+        return isWindows;
+    }
+
+    protected static boolean isMacintosh() {
+        boolean isMacintosh = false;
+        String osName = System.getProperty(OS_NAME, "");
+        if (osName.toLowerCase().indexOf("mac") >= 0) {
+            isMacintosh = true;
+        }
+        return isMacintosh;
+    }
+
+    protected static boolean isGNUJava() {
+        boolean isGNUJava = false;
+        String javaVmName = System.getProperty(JAVA_VM_NAME, "");
+        String lowerVmName = javaVmName.toLowerCase();
+        if (lowerVmName.indexOf("gnu") >= 0 && lowerVmName.indexOf("libgcj") >= 0) {
+            isGNUJava = true;
+        }
+        return isGNUJava;
+    }
+
+    protected static boolean isJDK141() {
+        boolean isJDK141 = false;
+        String javaVersion = System.getProperty(JavaVersionTester.JAVA_VERSION, "");
+        if (javaVersion.toLowerCase().startsWith("1.4.1")) {
+            isJDK141 = true;
+        }
+        return isJDK141;
+    }
+
+    /**
+     * Get the version info of an external (maybe other) jvm.
+     * 
+     * @param javaHomeHandler
+     *            The java home handler pointing to the java home of the external jvm.<br>
+     *            The /bin directory is assumed to be a direct child directory.
+     * 
+     * @return The versionInfo
+     */
+    protected static JavaVersionInfo getExternalJavaVersion(JavaHomeHandler javaHomeHandler) {
+        JavaVersionInfo versionInfo = new JavaVersionInfo();
+        if (javaHomeHandler.isValidHome()) {
+            try {
+                ConsoleInstaller.message(getText(TextKeys.C_CHECK_JAVA_VERSION));
+                // launch the java command - temporary file will be written by the child process
+                File tempFile = File.createTempFile("jython_installation", ".properties");
+                if (tempFile.exists() && tempFile.canWrite()) {
+                    String command[] = new String[5];
+                    command[0] = javaHomeHandler.getExecutableName();
+                    command[1] = "-cp";
+                    // our own class path should be ok here
+                    command[2] = System.getProperty("java.class.path"); 
+                    command[3] = JavaVersionTester.class.getName();
+                    command[4] = tempFile.getAbsolutePath();
+                    verboseOutput("executing: " + command[0] + " " + command[1] + " " + command[2]
+                            + " " + command[3] + " " + command[4]);
+                    ChildProcess childProcess = new ChildProcess(command, 10000); // 10 seconds
+                    childProcess.setDebug(Installation.isVerbose());
+                    int errorCode = childProcess.run();
+                    if (errorCode != NORMAL_RETURN) {
+                        versionInfo.setErrorCode(errorCode);
+                        versionInfo.setReason(getText(TextKeys.C_NO_VALID_JAVA, javaHomeHandler.toString()));
+                    } else {
+                        Properties tempProperties = new Properties();
+                        tempProperties.load(new FileInputStream(tempFile));
+                        fillJavaVersionInfo(versionInfo, tempProperties);
+                    }
+                } else {
+                    versionInfo.setErrorCode(ERROR_RETURN);
+                    versionInfo.setReason(getText(TextKeys.C_UNABLE_CREATE_TMPFILE, tempFile.getAbsolutePath()));
+                }
+            } catch (IOException e) {
+                versionInfo.setErrorCode(ERROR_RETURN);
+                versionInfo.setReason(getText(TextKeys.C_NO_VALID_JAVA, javaHomeHandler.toString()));
+            }
+        } else {
+            versionInfo.setErrorCode(ERROR_RETURN);
+            versionInfo.setReason(getText(TextKeys.C_NO_VALID_JAVA, javaHomeHandler.toString()));
+        }
+
+        return versionInfo;
+    }
+    
+    /**
+     * @return The system default java version
+     */
+    public static JavaVersionInfo getDefaultJavaVersion() {
+        JavaVersionInfo versionInfo = new JavaVersionInfo();
+        String executableName = "java";
+        try {
+            // launch the java command - temporary file will be written by the child process
+            File tempFile = File.createTempFile("jython_installation", ".properties");
+            if (tempFile.exists() && tempFile.canWrite()) {
+                String command[] = new String[5];
+                command[0] = executableName;
+                command[1] = "-cp";
+                // our own class path should be ok here
+                command[2] = System.getProperty("java.class.path");
+                command[3] = JavaVersionTester.class.getName();
+                command[4] = tempFile.getAbsolutePath();
+                ChildProcess childProcess = new ChildProcess(command, 10000); // 10 seconds
+                childProcess.setDebug(false);
+                int errorCode = childProcess.run();
+                if (errorCode != NORMAL_RETURN) {
+                    versionInfo.setErrorCode(errorCode);
+                    versionInfo.setReason(getText(TextKeys.C_NO_VALID_JAVA, executableName));
+                } else {
+                    Properties tempProperties = new Properties();
+                    tempProperties.load(new FileInputStream(tempFile));
+                    fillJavaVersionInfo(versionInfo, tempProperties);
+                }
+            } else {
+                versionInfo.setErrorCode(ERROR_RETURN);
+                versionInfo.setReason(getText(TextKeys.C_UNABLE_CREATE_TMPFILE,
+                                              tempFile.getAbsolutePath()));
+            }
+        } catch (IOException e) {
+            versionInfo.setErrorCode(ERROR_RETURN);
+            versionInfo.setReason(getText(TextKeys.C_NO_VALID_JAVA, executableName));
+        }
+        return versionInfo;
+    }
+
+    protected static void fillJavaVersionInfo(JavaVersionInfo versionInfo, Properties properties) {
+        versionInfo.setVersion(properties.getProperty(JavaVersionTester.JAVA_VERSION));
+        versionInfo.setSpecificationVersion(properties.getProperty(JavaVersionTester.JAVA_SPECIFICATION_VERSION));
+        versionInfo.setVendor(properties.getProperty(JavaVersionTester.JAVA_VENDOR));
+    }
+
+    public static class JavaVersionInfo {
+        private String _version;
+        private String _specificationVersion;
+        private String _vendor;
+        private int _errorCode;
+        private String _reason;
+
+        protected JavaVersionInfo() {
+            _version = EMPTY;
+            _specificationVersion = EMPTY;
+            _errorCode = NORMAL_RETURN;
+            _reason = EMPTY;
+        }
+
+        protected void setVersion(String version) {
+            _version = version;
+        }
+
+        protected void setSpecificationVersion(String specificationVersion) {
+            _specificationVersion = specificationVersion;
+        }
+
+        protected void setVendor(String vendor) {
+            _vendor = vendor;
+        }
+
+        protected void setErrorCode(int errorCode) {
+            _errorCode = errorCode;
+        }
+
+        protected void setReason(String reason) {
+            _reason = reason;
+        }
+
+        protected String getVersion() {
+            return _version;
+        }
+
+        public String getSpecificationVersion() {
+            return _specificationVersion;
+        }
+
+        protected String getVendor() {
+            return _vendor;
+        }
+
+        public int getErrorCode() {
+            return _errorCode;
+        }
+
+        protected String getReason() {
+            return _reason;
+        }
+    }
+
+    protected static class JavaFilenameFilter implements FilenameFilter {
+        public boolean accept(File dir, String name) {
+            if (name.toLowerCase().startsWith("java")) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public static boolean isGuiAllowed() {
+        verboseOutput("checking gui availability");
+        if (Boolean.getBoolean(HEADLESS_PROPERTY_NAME)) {
+            verboseOutput(HEADLESS_PROPERTY_NAME + " is true");
+            return false;
+        } else if (GraphicsEnvironment.isHeadless()) {
+            verboseOutput("GraphicsEnvironment is headless");
+            return false;
+        } else {
+            try {
+                verboseOutput("trying to get the GraphicsEnvironment");
+                GraphicsEnvironment.getLocalGraphicsEnvironment();
+                verboseOutput("got the GraphicsEnvironment!");
+                return true;
+            } catch (Throwable t) {
+                verboseOutput("got the following exception:");
+                verboseOutput(t);
+                return false;
+            }
+        }
+    }
+
+    //
+    // private methods
+    //
+
+    private static boolean useGui(InstallerCommandLine commandLine) {
+        if (commandLine.hasConsoleOption() || commandLine.hasSilentOption()) {
+            return false;
+        }
+        return isGuiAllowed();
+    }
+
+    /**
+     * In the normal case, this method is called with <code>(args, null, null)</code>, see <code>main(args)</code>.
+     * <p>
+     * However, in autotesting mode (<code>commandLine.hasAutotestOption()</code>), we pass in Autotest and Tunnel,
+     * see <code>driverMain(args, autotest, tunnel)</code>.
+     * <p>
+     * This means that in autotesting mode this method will call itself (via <code>InstallationDriver.drive()</code>),
+     * but with different arguments.
+     */
+    private static void internalMain(String[] args, Autotest autotest, Tunnel tunnel) {
+        try {
+            setVerbose(InstallerCommandLine.hasVerboseOptionInArgs(args));
+            dumpSystemProperties();
+            verboseOutput("reading jar info");
+            JarInfo jarInfo = new JarInfo();
+            InstallerCommandLine commandLine = new InstallerCommandLine(jarInfo);
+            if (!commandLine.setArgs(args) || commandLine.hasHelpOption()) {
+                commandLine.printHelp();
+                System.exit(1);
+            } else {
+                if (commandLine.hasAutotestOption()) {
+                    verboseOutput("running autotests");
+                    _isAutotesting = true;
+                    InstallationDriver autotestDriver = new InstallationDriver(commandLine);
+                    autotestDriver.drive(); // ! reentrant into internalMain()
+                    _isAutotesting = false;
+                    ConsoleInstaller.message("\ncongratulations - autotests complete !");
+                    System.exit(0);
+                }
+                if (!useGui(commandLine)) {
+                    verboseOutput("using the console installer");
+                    ConsoleInstaller consoleInstaller = new ConsoleInstaller(commandLine, jarInfo);
+                    consoleInstaller.setTunnel(tunnel);
+                    consoleInstaller.install();
+                    if (!isAutotesting()) {
+                        System.exit(0);
+                    }
+                } else {
+                    verboseOutput("using the gui installer");
+                    new FrameInstaller(commandLine, jarInfo, autotest);
+                }
+            }
+        } catch (InstallationCancelledException ice) {
+            ConsoleInstaller.message((getText(TextKeys.INSTALLATION_CANCELLED)));
+            System.exit(1);
+        } catch (Throwable t) {
+            t.printStackTrace();
+            System.exit(1);
+        }
+    }
+    
+    private static void dumpSystemProperties() throws IOException {
+        if (isVerbose()) {
+            @SuppressWarnings("unchecked")
+            Enumeration<String> names = (Enumeration<String>)System.getProperties().propertyNames();
+            StringBuilder contents = new StringBuilder(400);
+            contents.append("Properties at the beginning of the Jython installation:\n\n");
+            while (names.hasMoreElements()) {
+                String name = names.nextElement();
+                String value = System.getProperty(name, "");
+                contents.append(name);
+                contents.append('=');
+                contents.append(value);
+                contents.append("\n");
+            }
+            File output = File.createTempFile("System", ".properties");
+            FileHelper.write(output, contents.toString());
+            ConsoleInstaller.message("system properties dumped to " + output.getAbsolutePath());
+        }
+    }
+    
+    private static void verboseOutput(String message) {
+        if (isVerbose()) {
+            ConsoleInstaller.message(message);
+        }
+    }
+    
+    private static void verboseOutput(Throwable t) {
+        if (isVerbose()) {
+            t.printStackTrace();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/InstallationCancelledException.java b/installer/src/java/org/python/util/install/InstallationCancelledException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/InstallationCancelledException.java
@@ -0,0 +1,9 @@
+package org.python.util.install;
+
+public class InstallationCancelledException extends InstallerException {
+
+    public InstallationCancelledException() {
+        super();
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/InstallationListener.java b/installer/src/java/org/python/util/install/InstallationListener.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/InstallationListener.java
@@ -0,0 +1,7 @@
+package org.python.util.install;
+
+public interface InstallationListener {
+
+    public void progressFinished();
+
+}
diff --git a/installer/src/java/org/python/util/install/InstallationType.java b/installer/src/java/org/python/util/install/InstallationType.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/InstallationType.java
@@ -0,0 +1,120 @@
+package org.python.util.install;
+
+public class InstallationType {
+
+    private boolean _installLibraryModules = true;
+    private boolean _installDemosAndExamples = true;
+    private boolean _installDocumentation = true;
+    private boolean _installSources = false;
+    private boolean _isStandalone = false;
+
+    public boolean installLibraryModules() {
+        return _installLibraryModules;
+    }
+
+    public boolean installDemosAndExamples() {
+        return _installDemosAndExamples;
+    }
+
+    public boolean installDocumentation() {
+        return _installDocumentation;
+    }
+
+    public boolean installSources() {
+        return _installSources;
+    }
+
+    public void addLibraryModules() {
+        _installLibraryModules = true;
+    }
+
+    public void removeLibraryModules() {
+        _installLibraryModules = false;
+    }
+
+    public void addDemosAndExamples() {
+        _installDemosAndExamples = true;
+    }
+
+    public void removeDemosAndExamples() {
+        _installDemosAndExamples = false;
+    }
+
+    public void addDocumentation() {
+        _installDocumentation = true;
+    }
+
+    public void removeDocumentation() {
+        _installDocumentation = false;
+    }
+
+    public void addSources() {
+        _installSources = true;
+    }
+
+    public void removeSources() {
+        _installSources = false;
+    }
+
+    public void setStandalone() {
+        _isStandalone = true;
+        addLibraryModules();
+        removeDemosAndExamples();
+        removeDocumentation();
+        removeSources();
+    }
+
+    public boolean isStandalone() {
+        return _isStandalone;
+    }
+
+    public void setAll() {
+        addLibraryModules();
+        addDemosAndExamples();
+        addDocumentation();
+        addSources();
+        _isStandalone = false;
+    }
+
+    public boolean isAll() {
+        return installLibraryModules() && installDemosAndExamples() && installDocumentation() && installSources();
+    }
+
+    public void setStandard() {
+        addLibraryModules();
+        addDemosAndExamples();
+        addDocumentation();
+        removeSources();
+        _isStandalone = false;
+    }
+
+    public boolean isStandard() {
+        return installLibraryModules() && installDemosAndExamples() && installDocumentation() && !installSources();
+    }
+
+    public void setMinimum() {
+        removeLibraryModules();
+        removeDemosAndExamples();
+        removeDocumentation();
+        removeSources();
+        _isStandalone = false;
+    }
+
+    public boolean isMinimum() {
+        return !installLibraryModules() && !installDemosAndExamples() && !installDocumentation() && !installSources();
+    }
+
+    /**
+     * @return <code>true</code> if current settings reflect one of the predefined settings
+     */
+    public boolean isPredefined() {
+        return isAll() || isStandard() || isMinimum() || isStandalone();
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer(30);
+        buf.append("mod: " + installDemosAndExamples() + ", demo: " + installDemosAndExamples() + ", doc: "
+                + installDocumentation() + ", src: " + installSources());
+        return buf.toString();
+    }
+}
diff --git a/installer/src/java/org/python/util/install/InstallerCommandLine.java b/installer/src/java/org/python/util/install/InstallerCommandLine.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/InstallerCommandLine.java
@@ -0,0 +1,456 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.MissingArgumentException;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.Parser;
+import org.apache.commons.cli.PosixParser;
+
+public class InstallerCommandLine {
+    protected static final String INEXCLUDE_LIBRARY_MODULES = "mod";
+    protected static final String INEXCLUDE_DEMOS_AND_EXAMPLES = "demo";
+    protected static final String INEXCLUDE_DOCUMENTATION = "doc";
+    protected static final String INEXCLUDE_SOURCES = "src";
+
+    protected static final String CONSOLE_SHORT = "c";
+    protected static final String CONSOLE_LONG = "console";
+    private static final String CONSOLE_DESC = "console based installation (user interaction)\n"
+            + "any other options will be ignored (except 'verbose')";
+
+    protected static final String SILENT_SHORT = "s";
+    protected static final String SILENT_LONG = "silent";
+    private static final String SILENT_DESC = "silent installation (without user interaction)";
+
+    protected static final String VERBOSE_SHORT = "v";
+    protected static final String VERBOSE_LONG = "verbose";
+    private static final String VERBOSE_DESC = "print more output during the installation\n"
+            + "(also valid in GUI and autotest mode)";
+
+    private static final String JRE_SHORT = "j";
+    private static final String JRE_LONG = "jre";
+    private static final String JRE_DESC = "home directory of the runtime jre or jdk\n"
+            + "(executables are assumed in the /bin subdirectory)\n" + "select this if you want to run Jython with a\n"
+            + "different java version than the installation";
+
+    private static final String AUTOTEST_SHORT = "A";
+    private static final String AUTOTEST_LONG = "autotest";
+    private static final String AUTOTEST_DESC = "automatic stress tests for the installer\n"
+            + "most of the other options are ignored\n" + "allowed additional options: '" + VERBOSE_LONG + "', '"
+            + JRE_LONG + "'";
+
+    private static final String DIRECTORY_SHORT = "d";
+    private static final String DIRECTORY_LONG = "directory";
+    private static final String DIRECTORY_DESC = "target directory to install to\n"
+            + "(required in silent mode,\nused as default in GUI mode)";
+
+    private static final String DIRECTORY_ARG = "dir";
+
+    private static final String TYPE_STANDARD = "standard";
+    private static final String TYPE_ALL = "all";
+    private static final String TYPE_MINIMUM = "minimum";
+    protected static final String TYPE_STANDALONE = "standalone";
+    private static final String STANDALONE_DOCUMENTATION = "install a single, executable .jar,\ncontaining all the modules";
+
+    private static final String INEXCLUDE_ARG = "part(s)";
+    private static final String INEXCLUDE_PARTS = "more than one of the following is possible:\n" + "- "
+            + INEXCLUDE_LIBRARY_MODULES + ": library modules\n" + "- " + INEXCLUDE_DEMOS_AND_EXAMPLES
+            + ": demos and examples\n" + "- " + INEXCLUDE_DOCUMENTATION + ": documentation\n" + "- "
+            + INEXCLUDE_SOURCES + ": java source code";
+
+    private static final String TYPE_SHORT = "t";
+    private static final String TYPE_LONG = "type";
+    private static final String TYPE_ARG = TYPE_LONG;
+    private static final String TYPE_DESC = "installation type\n" + "one of the following types is possible\n"
+            + "(see also include/exclude parts):\n" + "- " + TYPE_ALL + ": everything (including " + INEXCLUDE_SOURCES
+            + ")\n" + "- " + TYPE_STANDARD + ": core, " + INEXCLUDE_LIBRARY_MODULES + ", "
+            + INEXCLUDE_DEMOS_AND_EXAMPLES + ", " + INEXCLUDE_DOCUMENTATION + ",\n"+ TYPE_STANDARD+ " is the default\n" + "- " + TYPE_MINIMUM + ": core\n"
+            + "- " + TYPE_STANDALONE + ": " + STANDALONE_DOCUMENTATION;
+
+    private static final String INCLUDE_SHORT = "i";
+    private static final String INCLUDE_LONG = "include";
+    private static final String INCLUDE_DESC = "finer control over parts to install\n" + INEXCLUDE_PARTS;
+
+    private static final String EXCLUDE_SHORT = "e";
+    private static final String EXCLUDE_LONG = "exclude";
+    private static final String EXCLUDE_DESC = "finer control over parts not to install\n" + INEXCLUDE_PARTS
+            + "\n(excludes override includes)";
+
+    private static final String HELP_SHORT = "h";
+    private static final String HELP2_SHORT = "?";
+    private static final String HELP_LONG = "help";
+    private static final String HELP_DESC = "print this help (overrides any other options)";
+
+    private static final String SYNTAX = "\n\tjava -jar jython_version.jar";
+    private static final String HEADER = "\nNo option at all will start the interactive GUI installer, except:\n"
+            + "Options respected in GUI mode are '" + DIRECTORY_LONG + "' and '" + JRE_LONG
+            + "', which serve as default values in the wizard.\n"
+            + "In non-GUI mode the following options are available:\n.";
+    private static final String SYNTAX_WITHOUT_JAR = "\n\tjava -jar ";
+    private static final String FOOTER = "";
+    private static final String EXAMPLES = "\nexample of a GUI installation:{0}"
+            + "\n\nexample of a console installation:{0} -" + CONSOLE_SHORT
+            + "\n\nexample of a silent installation:{0} -" + SILENT_SHORT + " -" + DIRECTORY_SHORT + " targetDirectory"
+            + "\n\nexamples of a silent installation with more options:{0} -" + SILENT_SHORT + " -" + DIRECTORY_SHORT
+            + " targetDirectory -" + TYPE_SHORT + " " + TYPE_MINIMUM + " -" + INCLUDE_SHORT + " " + INEXCLUDE_SOURCES
+            + " -" + JRE_SHORT + " javaHome" + "{0} -" + SILENT_SHORT + " -" + DIRECTORY_SHORT + " targetDirectory -"
+            + TYPE_SHORT + " " + TYPE_STANDARD + " -" + EXCLUDE_SHORT + " " + INEXCLUDE_DEMOS_AND_EXAMPLES + " "
+            + INEXCLUDE_DOCUMENTATION + "\n\t\t -" + INCLUDE_SHORT + " " + INEXCLUDE_SOURCES + " -" + JRE_SHORT
+            + " javaHome -" + VERBOSE_SHORT
+            + "\n\nexample of an autotest installation into temporary directories:{0} -" + AUTOTEST_SHORT
+            + "\n\t(make sure you do NOT touch mouse NOR keyboard after hitting enter/return!)"
+            + "\n\nexample of an autotest installation, using a different jre for the start scripts:{0} -"
+            + AUTOTEST_SHORT + " -" + JRE_SHORT + " javaHome" + " -" + VERBOSE_SHORT
+            + "\n\t(make sure you do NOT touch mouse NOR keyboard after hitting enter/return!)";
+
+    private String[] _args;
+    private Options _options;
+    private CommandLine _commandLine;
+    private JarInfo _jarInfo;
+    private final Parser _parser = new PosixParser();
+
+    public InstallerCommandLine(JarInfo jarInfo) {
+        createOptions();
+        _jarInfo = jarInfo;
+    }
+
+    /**
+     * Pre-scan of the arguments to detect a verbose flag
+     * @param args 
+     * @return <code>true</code> if there is a verbose option
+     */
+    public static final boolean hasVerboseOptionInArgs(String[] args) {
+        String shortVerbose = "-".concat(VERBOSE_SHORT);
+        String longVerbose = "--".concat(VERBOSE_LONG);
+        return hasOptionInArgs(args, shortVerbose, longVerbose);
+    }
+
+    /**
+     * constructor intended for JUnit tests only.
+     */
+    public InstallerCommandLine() {
+        this(null);
+    }
+
+    /**
+     * Set the arguments from the command line.
+     * 
+     * @param args the arguments of the command line
+     * @return <code>true</code> if all arguments are valid, <code>false</code> otherwise. No help is printed if
+     * <code>false</code> is returned
+     */
+    public boolean setArgs(String args[]) {
+        // pre-process args to determine if we can (and should) switch to console mode
+        try {
+            CommandLine preCommandLine = _parser.parse(_options, args, false);
+            if (!hasConsoleOption(preCommandLine) && !hasSilentOption(preCommandLine)
+                    && !hasAutotestOption(preCommandLine)) {
+                if (!Installation.isGuiAllowed() || Installation.isGNUJava()) {
+                    // auto switch to console mode
+                    if (hasVerboseOption(preCommandLine)) {
+                        ConsoleInstaller.message("auto-switching to console mode");
+                    }
+                    String[] newArgs = new String[args.length + 1];
+                    System.arraycopy(args, 0, newArgs, 0, args.length);
+                    newArgs[args.length] = "-" + CONSOLE_SHORT;
+                    args = newArgs;
+                }
+            }
+        } catch (Exception e) {
+            // ignore
+        }
+        _args = args;
+        try {
+            // throws for missing or unknown options / arguments
+            _commandLine = _parser.parse(_options, _args, false);
+        } catch (MissingArgumentException mae) {
+            System.err.println(mae.getMessage());
+            return false;
+        } catch (ParseException pe) {
+            System.err.println(pe.getMessage());
+            return false;
+        }
+        List unrecognized = _commandLine.getArgList();
+        if (unrecognized.size() > 0) {
+            System.err.println("unrecognized argument(s): " + unrecognized);
+            return false;
+        }
+        if (hasTypeOption()) {
+            String type = _commandLine.getOptionValue(TYPE_SHORT);
+            if (TYPE_ALL.equals(type) || TYPE_STANDARD.equals(type) || TYPE_MINIMUM.equals(type)
+                    || TYPE_STANDALONE.equals(type)) {
+            } else {
+                System.err.println("unrecognized argument '" + type + "' to option: " + TYPE_SHORT + " / " + TYPE_LONG);
+                return false;
+            }
+        }
+        if (hasSilentOption()) {
+            if (!hasDirectoryOption()) {
+                System.err.println("option " + DIRECTORY_SHORT + " / " + DIRECTORY_LONG + " is required in "
+                        + SILENT_LONG + " mode");
+                return false;
+            }
+        }
+        if (hasIncludeOption()) {
+            String[] includeParts = _commandLine.getOptionValues(INCLUDE_SHORT);
+            for (int i = 0; i < includeParts.length; i++) {
+                if (!isValidInExcludePart(includeParts[i])) {
+                    System.err.println("unrecognized include part '" + includeParts[i] + "'");
+                    return false;
+                }
+            }
+        }
+        if (hasExcludeOption()) {
+            String[] excludeParts = _commandLine.getOptionValues(EXCLUDE_SHORT);
+            for (int i = 0; i < excludeParts.length; i++) {
+                if (!isValidInExcludePart(excludeParts[i])) {
+                    System.err.println("unrecognized exclude part '" + excludeParts[i] + "'");
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public boolean hasArguments() {
+        return _args.length > 0;
+    }
+
+    public boolean hasHelpOption() {
+        return _commandLine.hasOption(HELP_SHORT) || _commandLine.hasOption(HELP2_SHORT)
+                || _commandLine.hasOption(HELP_LONG);
+    }
+
+    public boolean hasSilentOption() {
+        return hasSilentOption(_commandLine);
+    }
+
+    private boolean hasSilentOption(CommandLine commandLine) {
+        return commandLine.hasOption(SILENT_SHORT) || commandLine.hasOption(SILENT_LONG);
+    }
+
+    public boolean hasConsoleOption() {
+        return hasConsoleOption(_commandLine);
+    }
+    
+    private boolean hasConsoleOption(CommandLine commandLine) {
+        return commandLine.hasOption(CONSOLE_SHORT) || commandLine.hasOption(CONSOLE_LONG);
+    }
+
+    public boolean hasAutotestOption() {
+        return hasAutotestOption(_commandLine);
+    }
+    
+    private boolean hasAutotestOption(CommandLine commandLine) {
+        return commandLine.hasOption(AUTOTEST_SHORT) || commandLine.hasOption(AUTOTEST_LONG);
+    }
+
+    public boolean hasDirectoryOption() {
+        return _commandLine.hasOption(DIRECTORY_SHORT) || _commandLine.hasOption(DIRECTORY_LONG);
+    }
+
+    public boolean hasTypeOption() {
+        return _commandLine.hasOption(TYPE_SHORT) || _commandLine.hasOption(TYPE_LONG);
+    }
+
+    public boolean hasIncludeOption() {
+        return _commandLine.hasOption(INCLUDE_SHORT) || _commandLine.hasOption(INCLUDE_LONG);
+    }
+
+    public boolean hasExcludeOption() {
+        return _commandLine.hasOption(EXCLUDE_SHORT) || _commandLine.hasOption(EXCLUDE_LONG);
+    }
+
+    public boolean hasJavaHomeOption() {
+        return _commandLine.hasOption(JRE_SHORT) || _commandLine.hasOption(JRE_LONG);
+    }
+
+    public boolean hasVerboseOption() {
+        return hasVerboseOption(_commandLine);
+    }
+
+    private boolean hasVerboseOption(CommandLine commandLine) {
+        return commandLine.hasOption(VERBOSE_SHORT) || commandLine.hasOption(VERBOSE_LONG);
+    }
+
+    public void printHelp() {
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.defaultWidth = 76;
+        String syntax = SYNTAX;
+        if (_jarInfo != null) {
+            try {
+                syntax = SYNTAX_WITHOUT_JAR + _jarInfo.getJarFile().getName();
+            } catch (IOException ioe) {
+            }
+        }
+        formatter.printHelp(syntax, HEADER, _options, FOOTER, true);
+        String examples = MessageFormat.format(EXAMPLES, syntax);
+        System.out.println(examples);
+    }
+
+    /**
+     * @return the requested target directory, <code>null</code> if no directory specified
+     */
+    public File getTargetDirectory() {
+        if (hasDirectoryOption()) {
+            return new File(_commandLine.getOptionValue(DIRECTORY_SHORT));
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @return a java home handler for the requested java home directory, or a default handler if no
+     *         java home specified
+     */
+    public JavaHomeHandler getJavaHomeHandler() {
+        if (hasJavaHomeOption()) {
+            return new JavaHomeHandler(_commandLine.getOptionValue(JRE_SHORT));
+        } else {
+            return new JavaHomeHandler();
+        }
+    }
+
+    /**
+     * The Installation type is built out of the type, include and exclude option
+     * 
+     * @return the installation type usable for the jar installer
+     */
+    public InstallationType getInstallationType() {
+        InstallationType installationType = new InstallationType(); // defaults to standard
+        // build a priori values out of the type option
+        if (hasTypeOption()) {
+            String typeName = _commandLine.getOptionValue(TYPE_SHORT);
+            if (TYPE_ALL.equals(typeName)) {
+                installationType.setAll();
+            } else if (TYPE_MINIMUM.equals(typeName)) {
+                installationType.setMinimum();
+            } else if (TYPE_STANDALONE.equals(typeName)) {
+                installationType.setStandalone();
+            }
+        }
+        // add parts to include
+        if (hasIncludeOption()) {
+            String[] includeParts = _commandLine.getOptionValues(INCLUDE_SHORT);
+            for (int i = 0; i < includeParts.length; i++) {
+                if (INEXCLUDE_DEMOS_AND_EXAMPLES.equals(includeParts[i])) {
+                    installationType.addDemosAndExamples();
+                }
+                if (INEXCLUDE_DOCUMENTATION.equals(includeParts[i])) {
+                    installationType.addDocumentation();
+                }
+                if (INEXCLUDE_LIBRARY_MODULES.equals(includeParts[i])) {
+                    installationType.addLibraryModules();
+                }
+                if (INEXCLUDE_SOURCES.equals(includeParts[i])) {
+                    installationType.addSources();
+                }
+            }
+        }
+        // remove parts to exclude
+        if (hasExcludeOption()) {
+            String[] excludeParts = _commandLine.getOptionValues(EXCLUDE_SHORT);
+            for (int i = 0; i < excludeParts.length; i++) {
+                if (INEXCLUDE_DEMOS_AND_EXAMPLES.equals(excludeParts[i])) {
+                    installationType.removeDemosAndExamples();
+                }
+                if (INEXCLUDE_DOCUMENTATION.equals(excludeParts[i])) {
+                    installationType.removeDocumentation();
+                }
+                if (INEXCLUDE_LIBRARY_MODULES.equals(excludeParts[i])) {
+                    installationType.removeLibraryModules();
+                }
+                if (INEXCLUDE_SOURCES.equals(excludeParts[i])) {
+                    installationType.removeSources();
+                }
+            }
+        }
+        return installationType;
+    }
+
+    //
+    // private methods
+    //
+    
+    private static final boolean hasOptionInArgs(String[] args, String shortOption, String longOption) {
+        boolean hasOption = false;
+        int i = 0;
+        while (!hasOption && i < args.length) {
+            if (shortOption.equals(args[i]) || longOption.equals(args[i])) {
+                hasOption = true;
+            }
+            i++;
+        }
+        return hasOption;
+    }
+
+    private void createOptions() {
+        _options = new Options();
+        _options.setSortAsAdded(true);
+
+        // console or silent mode
+        Option consoleOption = new Option(CONSOLE_SHORT, CONSOLE_LONG, false, CONSOLE_DESC);
+        Option silentOption = new Option(SILENT_SHORT, SILENT_LONG, false, SILENT_DESC);
+        Option autotestOption = new Option(AUTOTEST_SHORT, AUTOTEST_LONG, false, AUTOTEST_DESC);
+        OptionGroup group1 = new OptionGroup();
+        group1.addOption(consoleOption);
+        group1.addOption(silentOption);
+        group1.addOption(autotestOption);
+        _options.addOptionGroup(group1);
+
+        // target directory
+        Option directoryOption = new Option(DIRECTORY_SHORT, DIRECTORY_LONG, true, DIRECTORY_DESC);
+        directoryOption.setArgName(DIRECTORY_ARG);
+        _options.addOption(directoryOption);
+
+        // installation type
+        Option typeOption = new Option(TYPE_SHORT, TYPE_LONG, true, TYPE_DESC);
+        typeOption.setArgName(TYPE_ARG);
+        _options.addOption(typeOption);
+
+        // additional parts to include
+        Option includeOption = new Option(INCLUDE_SHORT, INCLUDE_DESC);
+        includeOption.setArgs(4);
+        includeOption.setArgName(INEXCLUDE_ARG);
+        includeOption.setLongOpt(INCLUDE_LONG);
+        _options.addOption(includeOption);
+
+        // parts to exclude
+        Option excludeOption = new Option(EXCLUDE_SHORT, EXCLUDE_DESC);
+        excludeOption.setArgs(4);
+        excludeOption.setArgName(INEXCLUDE_ARG);
+        excludeOption.setLongOpt(EXCLUDE_LONG);
+        _options.addOption(excludeOption);
+
+        // runtime jre
+        Option jreOption = new Option(JRE_SHORT, JRE_LONG, true, JRE_DESC);
+        jreOption.setArgName(DIRECTORY_ARG);
+        _options.addOption(jreOption);
+
+        // verbose
+        Option verboseOption = new Option(VERBOSE_SHORT, VERBOSE_LONG, false, VERBOSE_DESC);
+        _options.addOption(verboseOption);
+
+        // different help options
+        Option helpHOption = new Option(HELP_SHORT, HELP_LONG, false, HELP_DESC);
+        Option helpQOption = new Option(HELP2_SHORT, HELP_DESC);
+        OptionGroup group2 = new OptionGroup();
+        group2.addOption(helpHOption);
+        group2.addOption(helpQOption);
+        _options.addOptionGroup(group2);
+    }
+
+    private boolean isValidInExcludePart(String part) {
+        return INEXCLUDE_DEMOS_AND_EXAMPLES.equals(part) || INEXCLUDE_DOCUMENTATION.equals(part)
+                || INEXCLUDE_LIBRARY_MODULES.equals(part) || INEXCLUDE_SOURCES.equals(part);
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/InstallerException.java b/installer/src/java/org/python/util/install/InstallerException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/InstallerException.java
@@ -0,0 +1,21 @@
+package org.python.util.install;
+
+public class InstallerException extends RuntimeException {
+
+    public InstallerException() {
+        super();
+    }
+
+    public InstallerException(String message) {
+        super(message);
+    }
+
+    public InstallerException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public InstallerException(Throwable cause) {
+        super(cause);
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/JarInfo.java b/installer/src/java/org/python/util/install/JarInfo.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/JarInfo.java
@@ -0,0 +1,235 @@
+package org.python.util.install;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+public class JarInfo {
+    private static final String JAR_URL_PREFIX = "jar:file:";
+    private static final String JAR_SEPARATOR = "!";
+    private static final String JYTHON = "Jython";
+    private static final String VERSION_ATTRIBUTE = "version";
+    private static final String EXCLUDE_DIRS_ATTRIBUTE = "exclude-dirs";
+    private static final String EXCLUDE_DIRS_DELIM = ";";
+
+    private File _jarFile;
+    private int _numberOfEntries;
+    private Manifest _manifest;
+    private String _licenseText;
+    private String _readmeText;
+
+    public JarInfo() {
+        _jarFile = null;
+        _numberOfEntries = 0;
+        _manifest = null;
+
+        try {
+            readJarInfo();
+        } catch (IOException ioe) {
+            throw new InstallerException(Installation.getText(TextKeys.ERROR_ACCESS_JARFILE), ioe);
+        }
+    }
+
+    public String getVersion() {
+        String version = "<unknown>";
+        try {
+            Attributes jythonAttributes = getManifest().getAttributes(JYTHON);
+            if (jythonAttributes != null) {
+                version = jythonAttributes.getValue(VERSION_ATTRIBUTE); // do
+                // not
+                // use
+                // containsKey
+            }
+        } catch (IOException ioe) {
+        }
+        return version;
+    }
+
+    public File getJarFile() throws IOException {
+        if (_jarFile == null)
+            readJarInfo();
+        return _jarFile;
+    }
+
+    public Manifest getManifest() throws IOException {
+        if (_manifest == null)
+            readJarInfo();
+        return _manifest;
+    }
+
+    public int getNumberOfEntries() throws IOException {
+        if (_numberOfEntries == 0)
+            readJarInfo();
+        return _numberOfEntries;
+    }
+
+    public List<String> getExcludeDirs() throws IOException {
+        List<String> excludeDirs = new ArrayList<String>();
+        Attributes jythonAttributes = getManifest().getAttributes(JYTHON);
+        if (jythonAttributes != null) {
+            // do not use containsKey
+            String excludeDirsString = jythonAttributes.getValue(EXCLUDE_DIRS_ATTRIBUTE);
+            if (excludeDirsString != null && excludeDirsString.length() > 0) {
+                StringTokenizer tokenizer = new StringTokenizer(excludeDirsString, EXCLUDE_DIRS_DELIM);
+                while (tokenizer.hasMoreTokens()) {
+                    excludeDirs.add(tokenizer.nextToken());
+                }
+            }
+        }
+        return excludeDirs;
+    }
+
+    public String getLicenseText() throws IOException {
+        if (_licenseText == null) {
+            readJarInfo();
+        }
+        return _licenseText;
+    }
+
+    public String getReadmeText() throws IOException {
+        if (_readmeText == null) {
+            readJarInfo();
+        }
+        return _readmeText;
+    }
+
+    private void readJarInfo() throws IOException {
+        String fullClassName = getClass().getName();
+        String className = fullClassName.substring(fullClassName.lastIndexOf(".") + 1);
+        URL url = getClass().getResource(className + ".class");
+        // we expect an URL like:
+        // jar:file:/C:/stuff/jython21i.jar!/org/python/util/install/JarInfo.class
+        // escape plus signs, since the URLDecoder would turn them into spaces
+        final String plus = "\\+";
+        final String escapedPlus = "__ppluss__";
+        String rawUrl = url.toString();
+        rawUrl = rawUrl.replaceAll(plus, escapedPlus);
+        String urlString = URLDecoder.decode(rawUrl, "UTF-8");
+        urlString = urlString.replaceAll(escapedPlus, plus);
+        int jarSeparatorIndex = urlString.lastIndexOf(JAR_SEPARATOR);
+        if (!urlString.startsWith(JAR_URL_PREFIX) || jarSeparatorIndex <= 0) {
+            throw new InstallerException(Installation.getText(TextKeys.UNEXPECTED_URL, urlString));
+        }
+        String jarFileName = urlString.substring(JAR_URL_PREFIX.length(), jarSeparatorIndex);
+        _jarFile = new File(jarFileName);
+        if (!_jarFile.exists()) {
+            throw new InstallerException(Installation.getText(TextKeys.JAR_NOT_FOUND, _jarFile.getAbsolutePath()));
+        }
+        JarFile jarFile = new JarFile(_jarFile);
+        Enumeration<JarEntry> entries = jarFile.entries();
+        _numberOfEntries = 0;
+        while (entries.hasMoreElements()) {
+            JarEntry entry = (JarEntry) entries.nextElement();
+            if ("LICENSE.txt".equals(entry.getName())) {
+                _licenseText = readTextFile(entry, jarFile);
+            }
+            if ("README.txt".equals(entry.getName())) {
+                _readmeText = readTextFile(entry, jarFile);
+            }
+            _numberOfEntries++;
+        }
+        _manifest = jarFile.getManifest();
+        if (_manifest == null) {
+            throw new InstallerException(Installation.getText(TextKeys.NO_MANIFEST, _jarFile.getAbsolutePath()));
+        }
+        jarFile.close();
+    }
+
+    /**
+     * Read the text file with the most appropriate Charset.
+     * 
+     * @param entry
+     * @param jarFile
+     * 
+     * @return the contents of the text file
+     * 
+     * @throws IOException
+     */
+    private String readTextFile(JarEntry entry, JarFile jarFile) throws IOException {
+        String contents = readTextFileWithCharset(jarFile, entry, "US-ASCII"); // expected to run on most platforms
+        if (contents == null) {
+            contents = readTextFileWithCharset(jarFile, entry, "ISO-8859-1");
+        }
+        if (contents == null) {
+            contents = readTextFileWithDefaultCharset(jarFile, entry);
+        }
+        return contents;
+    }
+
+    /**
+     * Try to read the text file (jarEntry) from the jarFile, using a given <code>charsetName</code>.
+     * 
+     * @param jarFile
+     * @param entry
+     * @param charsetName the name of the Charset
+     * 
+     * @return the contents of the text file as String (if reading was successful), <code>null</code> otherwise.<br>
+     * No exception is thrown
+     */
+    private String readTextFileWithCharset(JarFile jarFile, JarEntry entry, String charsetName) {
+        String contents = null;
+        if (Charset.isSupported(charsetName)) {
+            BufferedReader reader = null;
+            try {
+                StringBuffer buffer = new StringBuffer(1000);
+                reader = new BufferedReader(new InputStreamReader(jarFile.getInputStream(entry), Charset
+                        .forName(charsetName)));
+                buffer = new StringBuffer(1000);
+                for (String s; (s = reader.readLine()) != null;) {
+                    buffer.append(s);
+                    buffer.append("\n");
+                }
+                contents = buffer.toString();
+            } catch (IOException ioe) {
+            } finally {
+                if (reader != null)
+                    try {
+                        reader.close();
+                    } catch (IOException e) {
+                    }
+            }
+        }
+        return contents;
+    }
+
+    /**
+     * Read the text file (jarEntry) from the jarFile, using the platform default Charset.
+     * 
+     * @param jarFile
+     * @param entry
+     * 
+     * @return the contents of the text file as String.
+     * 
+     * @throws IOException if a problem occurs
+     */
+    private String readTextFileWithDefaultCharset(JarFile jarFile, JarEntry entry) throws IOException {
+        String contents = null;
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new InputStreamReader(jarFile.getInputStream(entry)));
+            StringBuffer buffer = new StringBuffer(1000);
+            for (String s; (s = reader.readLine()) != null;) {
+                buffer.append(s);
+                buffer.append("\n");
+            }
+            contents = buffer.toString();
+        } finally {
+            if (reader != null)
+                reader.close();
+        }
+        return contents;
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/JarInstaller.java b/installer/src/java/org/python/util/install/JarInstaller.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/JarInstaller.java
@@ -0,0 +1,283 @@
+package org.python.util.install;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Working horse extracting the contents of the installation .jar to the file system. <br>
+ * The directory stucture is preserved, but there is the possibility to exclude some entries
+ * (directories at the moment).
+ */
+public class JarInstaller {
+
+    public static final String JYTHON_JAR = "jython.jar";
+
+    private static final String PATH_SEPARATOR = "/";
+
+    private static final String LIB_NAME_SEP = "Lib" + PATH_SEPARATOR;
+
+    private static final String LIB_PAWT_SEP = LIB_NAME_SEP + "pawt" + PATH_SEPARATOR;
+
+    private static final int BUFFER_SIZE = 1024;
+
+    private ProgressListener _progressListener;
+
+    private JarInfo _jarInfo;
+
+    private List<InstallationListener> _installationListeners;
+
+    public JarInstaller(ProgressListener progressListener, JarInfo jarInfo) {
+        _progressListener = progressListener;
+        _jarInfo = jarInfo;
+        _installationListeners = new ArrayList<InstallationListener>();
+    }
+
+    /**
+     * Do the pysical installation:
+     * <ul>
+     * <li>unzip the files
+     * <li>generate the start scripts
+     * </ul>
+     * 
+     * @param targetDirectory
+     * @param installationType
+     */
+    public void inflate(final File targetDirectory, InstallationType installationType, JavaHomeHandler javaHomeHandler) {
+        try {
+            // has to correspond with build.xml
+            // has to correspond with build.Lib.include.properties
+            List<String> excludeDirs = _jarInfo.getExcludeDirs();
+            List<String> coreLibFiles = new ArrayList<String>();
+            if (!installationType.installSources()) {
+                excludeDirs.add("src");
+                excludeDirs.add("grammar");
+                excludeDirs.add("extlibs");
+            }
+            if (!installationType.installDocumentation()) {
+                excludeDirs.add("Doc");
+            }
+            if (!installationType.installDemosAndExamples()) {
+                excludeDirs.add("Demo");
+            }
+            if (!installationType.installLibraryModules()) {
+                excludeDirs.add(LIB_NAME_SEP + "email");
+                excludeDirs.add(LIB_NAME_SEP + "encodings");
+                excludeDirs.add(LIB_NAME_SEP + "test");
+                excludeDirs.add(LIB_NAME_SEP + "jxxload_help");
+                coreLibFiles = getCoreLibFiles();
+            }
+            if (installationType.isStandalone()) {
+                excludeDirs.add("Tools");
+                excludeDirs.add(LIB_NAME_SEP + "email/test");
+                excludeDirs.add(LIB_NAME_SEP + "test");
+            }
+            int count = 0;
+            int percent = 0;
+            int numberOfIntervals = 100 / _progressListener.getInterval();
+            int numberOfEntries = approximateNumberOfEntries(installationType);
+            int threshold = numberOfEntries / numberOfIntervals + 1; // +1 = pessimistic
+            boolean coreExclusionReported = false;
+            // unzip
+            ZipInputStream zipInput = new ZipInputStream(new BufferedInputStream(new FileInputStream(_jarInfo.getJarFile()),
+                                                                                 BUFFER_SIZE));
+            ZipEntry zipEntry = zipInput.getNextEntry();
+            while (zipEntry != null) {
+                String zipEntryName = zipEntry.getName();
+                boolean exclude = false;
+                // handle exclusion of directories
+                Iterator<String> excludeDirsAsIterator = excludeDirs.iterator();
+                while (excludeDirsAsIterator.hasNext()) {
+                    if (zipEntryName.startsWith(excludeDirsAsIterator.next()
+                            + PATH_SEPARATOR)) {
+                        exclude = true;
+                    }
+                }
+                // exclude build.xml when not installing source
+                if (!installationType.installSources() && zipEntryName.equals("build.xml"))
+                    exclude = true;
+                // handle exclusion of core Lib files
+                if (!exclude) {
+                    exclude = shouldExcludeFile(installationType,
+                                                coreLibFiles,
+                                                zipEntry,
+                                                zipEntryName);
+                    if (Installation.isVerbose() && !coreExclusionReported && exclude) {
+                        ConsoleInstaller.message("excluding some .py files, like " + zipEntryName);
+                        coreExclusionReported = true;
+                    }
+                }
+                if (exclude) {
+                    if (Installation.isVerbose() && zipEntry.isDirectory()) {
+                        ConsoleInstaller.message("excluding directory " + zipEntryName);
+                    }
+                } else {
+                    count++;
+                    if (count % threshold == 0) {
+                        percent = percent + _progressListener.getInterval();
+                        _progressListener.progressChanged(percent);
+                    }
+                    createDirectories(targetDirectory, zipEntryName);
+                    if (!zipEntry.isDirectory()) {
+                        File file = createFile(targetDirectory, zipEntryName);
+                        _progressListener.progressEntry(file.getAbsolutePath());
+                        FileOutputStream output = new FileOutputStream(file);
+                        byte[] buffer = new byte[BUFFER_SIZE];
+                        int len;
+                        while ((len = zipInput.read(buffer)) > 0) {
+                            output.write(buffer, 0, len);
+                        }
+                        output.close();
+                        file.setLastModified(zipEntry.getTime());
+                    }
+                }
+                zipInput.closeEntry();
+                zipEntry = zipInput.getNextEntry();
+            }
+            if (!installationType.isStandalone()) {
+                // generate start scripts
+                _progressListener.progressStartScripts();
+                StartScriptGenerator generator = new StartScriptGenerator(targetDirectory, javaHomeHandler);
+                generator.generateStartScripts();
+            } else {
+                _progressListener.progressStandalone();
+                File jythonJar = new File(targetDirectory, JYTHON_JAR);
+                File jythonPlainJar = new File(targetDirectory, "plain_" + JYTHON_JAR);
+                jythonJar.renameTo(jythonPlainJar);
+                File libDir = new File(targetDirectory, "Lib");
+                StandalonePackager packager = new StandalonePackager(jythonJar);
+                packager.addJarFile(jythonPlainJar);
+                _progressListener.progressChanged(90); // approx
+                packager.addFullDirectory(libDir);
+                packager.close();
+                // TODO:Oti move to FileHelper
+                StandalonePackager.emptyDirectory(targetDirectory, jythonJar);
+            }
+            // finish: inform listeners
+            _progressListener.progressFinished();
+            Iterator<InstallationListener> installationListenersIterator = _installationListeners.iterator();
+            while (installationListenersIterator.hasNext()) {
+                installationListenersIterator.next().progressFinished();
+            }
+        } catch (IOException ioe) {
+            throw new InstallerException(Installation.getText(TextKeys.ERROR_ACCESS_JARFILE), ioe);
+        }
+    }
+
+    public void addInstallationListener(InstallationListener installationListener) {
+        if (installationListener != null) {
+            _installationListeners.add(installationListener);
+        }
+    }
+
+    private int approximateNumberOfEntries(InstallationType installationType) {
+        int numberOfEntries = 200; // core (minimum)
+        if (installationType.installLibraryModules()) {
+            if (installationType.isStandalone()) {
+                numberOfEntries += 450;
+            } else {
+                numberOfEntries += 1300;
+            }
+        }
+        if (installationType.installDemosAndExamples()) {
+            numberOfEntries += 70;
+        }
+        if (installationType.installDocumentation()) {
+            numberOfEntries += 500;
+        }
+        if (installationType.installSources()) {
+            numberOfEntries += 1000;
+        }
+        return numberOfEntries;
+    }
+
+    private void createDirectories(final File targetDirectory, final String zipEntryName) {
+        int lastSepIndex = zipEntryName.lastIndexOf(PATH_SEPARATOR);
+        if (lastSepIndex > 0) {
+            File directory = new File(targetDirectory, zipEntryName.substring(0, lastSepIndex));
+            if (directory.exists() && directory.isDirectory()) {} else {
+                if (!directory.mkdirs()) {
+                    throw new InstallerException(Installation.getText(TextKeys.UNABLE_CREATE_DIRECTORY,
+                                                                      directory.getAbsolutePath()));
+                }
+            }
+        }
+    }
+
+    private File createFile(final File targetDirectory, final String zipEntryName)
+            throws IOException {
+        File file = new File(targetDirectory, zipEntryName);
+        if (file.exists() && file.isFile()) {} else {
+            if (!file.createNewFile()) {
+                throw new InstallerException(Installation.getText(TextKeys.UNABLE_CREATE_FILE,
+                                                                  file.getCanonicalPath()));
+            }
+        }
+        return file;
+    }
+
+    private List<String> getCoreLibFiles() {
+        List<String> coreLibFiles = new ArrayList<String>();
+        coreLibFiles.add("__future__.py");
+        coreLibFiles.add("copy.py");
+        coreLibFiles.add("dbexts.py");
+        coreLibFiles.add("imaplib.py");
+        coreLibFiles.add("isql.py");
+        coreLibFiles.add("javaos.py");
+        coreLibFiles.add("javapath.py");
+        coreLibFiles.add("jreload.py");
+        coreLibFiles.add("marshal.py");
+        coreLibFiles.add("ntpath.py");
+        coreLibFiles.add("os.py");
+        coreLibFiles.add("popen2.py");
+        coreLibFiles.add("posixpath.py");
+        coreLibFiles.add("random.py");
+        coreLibFiles.add("re.py");
+        coreLibFiles.add("site.py");
+        coreLibFiles.add("socket.py");
+        coreLibFiles.add("sre.py");
+        coreLibFiles.add("sre_compile.py");
+        coreLibFiles.add("sre_constants.py");
+        coreLibFiles.add("sre_parse.py");
+        coreLibFiles.add("stat.py");
+        coreLibFiles.add("string.py");
+        coreLibFiles.add("threading.py");
+        coreLibFiles.add("UserDict.py");
+        coreLibFiles.add("zipfile.py");
+        coreLibFiles.add("zlib.py");
+        return coreLibFiles;
+    }
+
+    private boolean shouldExcludeFile(InstallationType installationType,
+                                      List<String> coreLibFiles,
+                                      ZipEntry zipEntry,
+                                      String zipEntryName) {
+        boolean exclude = false;
+        if (!installationType.installLibraryModules()) {
+            // handle files in Lib
+            if (!zipEntry.isDirectory() && zipEntryName.startsWith(LIB_NAME_SEP)) {
+                // include all files in /pawt subdirectory
+                if (!zipEntryName.startsWith(LIB_PAWT_SEP)) {
+                    if (zipEntryName.endsWith(".py")) { // only compare *.py files
+                        exclude = true;
+                        Iterator<String> coreLibFilesAsIterator = coreLibFiles.iterator();
+                        while (coreLibFilesAsIterator.hasNext()) {
+                            String coreFileName = coreLibFilesAsIterator.next();
+                            if (zipEntryName.endsWith(PATH_SEPARATOR + coreFileName)) {
+                                exclude = false;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return exclude;
+    }
+}
diff --git a/installer/src/java/org/python/util/install/JavaHomeHandler.java b/installer/src/java/org/python/util/install/JavaHomeHandler.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/JavaHomeHandler.java
@@ -0,0 +1,209 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Unified entry point for treatment of java.home
+ * <p>
+ * Note that the system property <code>java.home</code> is never changed
+ */
+public final class JavaHomeHandler {
+
+    public static final String JAVA_HOME = "java.home";
+
+    private static final String JAVA = "java";
+
+    private static final String JAVA_EXE = "java.exe";
+
+    private static final String BIN = "bin";
+
+    private static final String DEFAULT = "_default_";
+
+    private static final String EMPTY = "";
+
+    /**
+     * A map for java home strings and their respective executable names
+     */
+    private static Map<String, String> _executableNames;
+
+    /**
+     * The current java home
+     */
+    private String _currentJavaHome;
+
+    /**
+     * create a java home handler for the default java home
+     */
+    public JavaHomeHandler() {
+        this(DEFAULT);
+    }
+
+    /**
+     * create a java home handler for a java home deviation
+     * 
+     * @param currentJavaHome
+     *            The deviated java home
+     */
+    public JavaHomeHandler(String currentJavaHome) {
+        setCurrentJavaHome(currentJavaHome);
+        check(getCurrentJavaHome());
+    }
+
+    /**
+     * get the name of the java executable
+     * 
+     * @return A name of a java executable which can be passed to {@link ChildProcess}
+     */
+    public String getExecutableName() {
+        return getExecutableName(getCurrentJavaHome());
+    }
+
+    /**
+     * tell the validity of the current java home
+     * <p>
+     * Note: if the current java home is not valid, {@link JavaHomeHandler#getExecutableName()}
+     * still returns the name of a callable java
+     * 
+     * @return <code>true</code> if we have a valid java home, <code>false</code> otherwise.
+     */
+    public boolean isValidHome() {
+        return !getFallbackExecutableName().equals(getExecutableName());
+    }
+
+    /**
+     * get the current java home, if it is valid
+     * 
+     * @return The current java home
+     * @throws InstallerException
+     *             if there is no valid java home
+     * 
+     * @see JavaHomeHandler#isValidHome()
+     */
+    public File getHome() throws InstallerException {
+        if (!isValidHome()) {
+            throw new InstallerException("no valid java home");
+        } else {
+            return new File(getCurrentJavaHome());
+        }
+    }
+
+    /**
+     * @return <code>true</code> if the current java home is a deviation, <code>false</code>
+     *         otherwise
+     */
+    public boolean isDeviation() {
+        // make sure the default java home is also known
+        if (!getExecutableNames().containsKey(DEFAULT)) {
+            check(DEFAULT);
+        }
+        return !getExecutableName(DEFAULT).equals(getExecutableName(getCurrentJavaHome()));
+    }
+    
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(80);
+        builder.append("[");
+        if(!isValidHome()) {
+            builder.append("in");
+        }
+        builder.append("valid java home: ");
+        builder.append(getCurrentJavaHome());
+        builder.append("; executable: ");
+        builder.append(getExecutableName());
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /**
+     * reset the handler (clear all stored java homes)
+     */
+    static void reset() {
+        getExecutableNames().clear();
+    }
+
+    private String getExecutableName(String javaHome) {
+        Map<String, String> executableNames = getExecutableNames();
+        if (!executableNames.containsKey(javaHome)) {
+            check(javaHome);
+        }
+        return executableNames.get(javaHome);
+    }
+
+    private void check(String javaHome) {
+        boolean valid = false;
+        boolean isDefault = false;
+        File javaExecutableFile = null;
+        if (DEFAULT.equals(javaHome)) {
+            isDefault = true;
+            javaHome = System.getProperty(JAVA_HOME, EMPTY);
+        }
+        if (javaHome.length() > 0) {
+            File javaHomeDir = new File(javaHome);
+            if (javaHomeDir.exists() && javaHomeDir.isDirectory()) {
+                File binDir = new File(javaHomeDir, BIN);
+                if (binDir.exists() && binDir.isDirectory()) {
+                    javaExecutableFile = getExecutableFile(binDir);
+                    if (javaExecutableFile.exists()) {
+                        valid = true;
+                    }
+                }
+            }
+        }
+        if (valid) {
+            addExecutable(javaHome, javaExecutableFile);
+            if (isDefault) {
+                addExecutable(DEFAULT, javaExecutableFile);
+                if (DEFAULT.equals(getCurrentJavaHome())) {
+                    // update the current home to the real one
+                    setCurrentJavaHome(javaHome);
+                }
+            }
+        } else {
+            addFallbackExecutable(javaHome);
+            if (isDefault) {
+                addFallbackExecutable(DEFAULT);
+            }
+        }
+    }
+
+    private String getFallbackExecutableName() {
+        return JAVA;
+    }
+
+    private void addFallbackExecutable(String javaHome) {
+        getExecutableNames().put(javaHome, getFallbackExecutableName());
+    }
+
+    private void addExecutable(String javaHome, File javaExecutableFile) {
+        getExecutableNames().put(javaHome, javaExecutableFile.getAbsolutePath());
+    }
+
+    private File getExecutableFile(File binDir) {
+        if (Installation.isWindows()) {
+            return new File(binDir, JAVA_EXE);
+        } else {
+            return new File(binDir, JAVA);
+        }
+    }
+
+    private static Map<String, String> getExecutableNames() {
+        if (_executableNames == null) {
+            _executableNames = new HashMap<String, String>();
+        }
+        return _executableNames;
+    }
+
+    private String getCurrentJavaHome() {
+        if (_currentJavaHome == null) {
+            return DEFAULT;
+        } else {
+            return _currentJavaHome;
+        }
+    }
+
+    private void setCurrentJavaHome(String currentJavaHome) {
+        _currentJavaHome = currentJavaHome.trim();
+    }
+}
diff --git a/installer/src/java/org/python/util/install/JavaSelectionPage.java b/installer/src/java/org/python/util/install/JavaSelectionPage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/JavaSelectionPage.java
@@ -0,0 +1,199 @@
+package org.python.util.install;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.io.File;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+
+public class JavaSelectionPage extends AbstractWizardPage {
+
+    private static final long serialVersionUID = 2871052924519223110L;
+
+    private final static String _CURRENT_ACTION_COMMAND = "current";
+    private final static String _OTHER_ACTION_COMMAND = "other";
+
+    private JRadioButton _currentButton;
+    private JRadioButton _otherButton;
+
+    private JLabel _label;
+    private JTextField _javaHome;
+    private JButton _browse;
+
+    public JavaSelectionPage() {
+        super();
+        initComponents();
+    }
+
+    private void initComponents() {
+        // label for java home
+        _label = new JLabel();
+
+        // radio buttons
+        RadioButtonListener radioButtonListener = new RadioButtonListener();
+        _currentButton = new JRadioButton();
+        _currentButton.setActionCommand(_CURRENT_ACTION_COMMAND);
+        _currentButton.addActionListener(radioButtonListener);
+        _otherButton = new JRadioButton();
+        _otherButton.setActionCommand(_OTHER_ACTION_COMMAND);
+        _otherButton.addActionListener(radioButtonListener);
+        ButtonGroup radioButtonGroup = new ButtonGroup();
+        radioButtonGroup.add(_currentButton);
+        radioButtonGroup.add(_otherButton);
+        JPanel radioPanel = new JPanel(new GridLayout(0, 1));
+        radioPanel.add(_currentButton);
+        radioPanel.add(_otherButton);
+
+        // directory for java home
+        _javaHome = new JTextField(40);
+        _javaHome.addFocusListener(new JavaFocusListener());
+        // browse button
+        _browse = new JButton();
+        _browse.addActionListener(new BrowseButtonListener());
+
+        JPanel panel = new JPanel();
+        GridBagLayout gridBagLayout = new GridBagLayout();
+        panel.setLayout(gridBagLayout);
+        GridBagConstraints gridBagConstraints = newGridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        panel.add(_label, gridBagConstraints);
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        panel.add(radioPanel, gridBagConstraints);
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        panel.add(_javaHome, gridBagConstraints);
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 2;
+        panel.add(_browse, gridBagConstraints);
+
+        add(panel);
+    }
+
+    JTextField getJavaHome() {
+        return _javaHome;
+    }
+
+    protected String getTitle() {
+        return getText(TARGET_JAVA_HOME_PROPERTY);
+    }
+
+    protected String getDescription() {
+        return getText(CHOOSE_JRE);
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return true;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        return _currentButton;
+    }
+
+    protected void activate() {
+        _label.setText(getText(SELECT_JAVA_HOME) + ": ");
+        _currentButton.setText(getText(CURRENT));
+        _otherButton.setText(getText(OTHER));
+        _browse.setText(getText(BROWSE));
+        setValues();
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+    private void setValues() {
+        boolean current = true;
+        JavaHomeHandler javaHomeHandler = FrameInstaller.getJavaHomeHandler();
+        if (javaHomeHandler.isDeviation()) {
+            current = false;
+        }
+        setCurrent(current);
+    }
+
+    private void setCurrent(boolean current) {
+        if (current) {
+            FrameInstaller.setJavaHomeHandler(new JavaHomeHandler());
+            _currentButton.setSelected(true);
+            _otherButton.setSelected(false);
+            _javaHome.setEnabled(false);
+            _browse.setEnabled(false);
+        } else {
+            _currentButton.setSelected(false);
+            _otherButton.setSelected(true);
+            _javaHome.setEnabled(true);
+            _browse.setEnabled(true);
+        }
+        JavaHomeHandler javaHomeHandler = FrameInstaller.getJavaHomeHandler();
+        if (javaHomeHandler.isValidHome()) {
+            _javaHome.setText(javaHomeHandler.getHome().getAbsolutePath());
+        } else {
+            _javaHome.setText("");
+        }
+        _javaHome.setToolTipText(_javaHome.getText());
+    }
+
+    private final class BrowseButtonListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+            JFileChooser fileChooser = new JFileChooser(new File(_javaHome.getText()));
+            fileChooser.setDialogTitle(getText(SELECT_JAVA_HOME));
+            // the filter is at the moment only used for the title of the dialog:
+            fileChooser.setFileFilter(new DirectoryFilter());
+            fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+            if (fileChooser.isAcceptAllFileFilterUsed()) {
+                if (Installation.isMacintosh() && Installation.isJDK141()) {
+                    // work around ArrayIndexOutOfBoundsExceptio on Mac OS X, java version 1.4.1
+                } else {
+                    fileChooser.setAcceptAllFileFilterUsed(false);
+                }
+            }
+            int returnValue = fileChooser.showDialog(_browse, getText(SELECT));
+            if (returnValue == JFileChooser.APPROVE_OPTION) {
+                FrameInstaller.setJavaHomeHandler(new JavaHomeHandler(fileChooser.getSelectedFile().getAbsolutePath()));
+                setValues();
+            }
+        }
+    }
+
+    private final class RadioButtonListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+            String actionCommand = e.getActionCommand();
+            setCurrent(_CURRENT_ACTION_COMMAND.equals(actionCommand));
+        }
+    }
+
+    private final class JavaFocusListener implements FocusListener {
+        public void focusGained(FocusEvent e) {
+        }
+
+        public void focusLost(FocusEvent e) {
+            String javaHome = _javaHome.getText();
+            FrameInstaller.setJavaHomeHandler(new JavaHomeHandler(javaHome));
+            _javaHome.setToolTipText(javaHome);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/JavaSelectionPageValidator.java b/installer/src/java/org/python/util/install/JavaSelectionPageValidator.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/JavaSelectionPageValidator.java
@@ -0,0 +1,31 @@
+package org.python.util.install;
+
+import org.python.util.install.Installation.JavaVersionInfo;
+
+public class JavaSelectionPageValidator extends AbstractWizardValidator {
+
+    JavaSelectionPage _page;
+
+    JavaSelectionPageValidator(JavaSelectionPage page) {
+        super();
+        _page = page;
+    }
+
+    protected void validate() throws ValidationException {
+        JavaVersionInfo javaVersionInfo = new JavaVersionInfo();
+        String directory = _page.getJavaHome().getText().trim(); // trim to be sure
+        JavaHomeHandler javaHomeHandler = new JavaHomeHandler(directory);
+        if(javaHomeHandler.isDeviation()) {
+            javaVersionInfo = Installation.getExternalJavaVersion(javaHomeHandler);
+            if (javaVersionInfo.getErrorCode() != Installation.NORMAL_RETURN) {
+                throw new ValidationException(javaVersionInfo.getReason());
+            }
+        } else {
+            // no experiments if current java is selected
+            Installation.fillJavaVersionInfo(javaVersionInfo, System.getProperties());
+        }
+        FrameInstaller.setJavaHomeHandler(javaHomeHandler);
+        FrameInstaller.setJavaVersionInfo(javaVersionInfo);
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/JavaVersionTester.java b/installer/src/java/org/python/util/install/JavaVersionTester.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/JavaVersionTester.java
@@ -0,0 +1,61 @@
+package org.python.util.install;
+
+import java.io.File;
+
+/**
+ * Helper class to test a java version
+ */
+public class JavaVersionTester {
+
+    public static final String JAVA_HOME = "java.home";
+    protected static final String JAVA_VERSION = "java.version";
+    public static final String JAVA_SPECIFICATION_VERSION = "java.specification.version";
+    protected static final String JAVA_VENDOR = "java.vendor";
+    private static final String NEWLINE = "\n";
+
+    private static final String UNKNOWN = "<unknown>";
+
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            String tempFilePath = args[0];
+            File tempFile = new File(tempFilePath);
+            if (tempFile.exists() && tempFile.canWrite()) {
+                try {
+                    writeTempFile(tempFile);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    System.exit(1);
+                }
+            } else {
+                if (!tempFile.exists()) {
+                    System.err.println("temp file " + tempFilePath + " does not exist");
+                } else {
+                    System.err.println("cannot write to temp file " + tempFilePath);
+                }
+                System.exit(1);
+            }
+        } else {
+            System.err.println("no temp file given. usage: JavaVersionTester tempfile");
+            System.out.println("exiting with 1");
+            System.exit(1);
+        }
+    }
+
+    private static void writeTempFile(File file) throws Exception {
+        FileHelper.write(file, createFileContent());
+    }
+
+    private static String createFileContent() {
+        StringBuffer sb = new StringBuffer(500);
+        String java_home = new JavaHomeHandler().getExecutableName();
+        if (File.separatorChar != '/') {
+            java_home = java_home.replace(File.separatorChar, '/'); // backslash would be interpreted as escape char
+        }
+        sb.append(JAVA_HOME + "=" + java_home + NEWLINE);
+        sb.append(JAVA_VERSION + "=" + System.getProperty(JAVA_VERSION, UNKNOWN) + NEWLINE);
+        sb.append(JAVA_SPECIFICATION_VERSION + "=" + System.getProperty(JAVA_SPECIFICATION_VERSION, UNKNOWN) + NEWLINE);
+        sb.append(JAVA_VENDOR + "=" + System.getProperty(JAVA_VENDOR, UNKNOWN) + NEWLINE);
+        return sb.toString();
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/LanguagePage.java b/installer/src/java/org/python/util/install/LanguagePage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/LanguagePage.java
@@ -0,0 +1,121 @@
+package org.python.util.install;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+
+public class LanguagePage extends AbstractWizardPage {
+
+    private static Map _languageIndexes = new HashMap(2);
+    static {
+        _languageIndexes.put(Locale.ENGLISH, new Integer(0));
+        _languageIndexes.put(Locale.GERMAN, new Integer(1));
+    }
+
+    private JLabel _label;
+    private JComboBox _languageBox;
+    private JarInfo _jarInfo;
+    private boolean _activated;
+    private boolean _stopListening;
+
+    public LanguagePage(JarInfo jarInfo) {
+        super();
+        _jarInfo = jarInfo;
+        _activated = false;
+        _stopListening = false;
+        initComponents();
+    }
+
+    private void initComponents() {
+        _label = new JLabel();
+        add(_label);
+        _languageBox = new JComboBox();
+        _languageBox.addActionListener(new LanguageBoxListener());
+        add(_languageBox);
+    }
+
+    protected String getTitle() {
+        return getText(WELCOME_TO_JYTHON);
+    }
+
+    protected String getDescription() {
+        return getText(VERSION_INFO, _jarInfo.getVersion());
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return false;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        return _languageBox;
+    }
+
+    protected void activate() {
+        _label.setText(getText(SELECT_LANGUAGE) + ": ");
+        // replace combo box items (localization)
+        int itemCount = _languageBox.getItemCount();
+        _stopListening = true; // adding and removing fires an action event
+        for (int i = 0; i < itemCount; i++) {
+            _languageBox.removeItemAt(0);
+        }
+        _languageBox.addItem(getText(ENGLISH)); // keep indexes here
+        _languageBox.addItem(getText(GERMAN));
+        _stopListening = false;
+        if (!_activated) {
+            // preselect German if default looks like German
+            if (Locale.getDefault().toString().startsWith(Locale.GERMAN.toString())) {
+                _languageBox.setSelectedIndex(getLanguageIndex(Locale.GERMAN));
+                FrameInstaller.setLanguage(Locale.GERMAN);
+            }
+        } else {
+            _languageBox.setSelectedIndex(getLanguageIndex(FrameInstaller.getLanguage()));
+        }
+        _activated = true;
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+    private int getLanguageIndex(Locale locale) {
+        return ((Integer) _languageIndexes.get(locale)).intValue();
+    }
+
+    private Locale getLanguageFromIndex(int index) {
+        Integer indexInteger = new Integer(index);
+        Iterator languages = _languageIndexes.entrySet().iterator();
+        while (languages.hasNext()) {
+            Map.Entry entry = (Map.Entry) languages.next();
+            if (entry.getValue().equals(indexInteger)) {
+                return (Locale) entry.getKey();
+            }
+        }
+        return null;
+    }
+
+    private class LanguageBoxListener implements ActionListener {
+        public void actionPerformed(ActionEvent ae) {
+            if (!_stopListening) {
+                FrameInstaller.setLanguage(getLanguageFromIndex(_languageBox.getSelectedIndex()));
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/LicensePage.java b/installer/src/java/org/python/util/install/LicensePage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/LicensePage.java
@@ -0,0 +1,120 @@
+package org.python.util.install;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+public class LicensePage extends AbstractWizardPage {
+
+    private static final String _ACCEPT_ACTION_COMMAND = "1";
+    private static final String _DO_NOT_ACCEPT_ACTION_COMMAND = "0";
+
+    private JRadioButton _acceptButton;
+    private JRadioButton _doNotAcceptButton;
+
+    public LicensePage(JarInfo jarInfo) {
+        super();
+        initComponents(jarInfo);
+    }
+
+    private void initComponents(JarInfo jarInfo) {
+        String licenseText = "n/a";
+        try {
+            licenseText = jarInfo.getLicenseText();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        JPanel centerPanel = new JPanel();
+        centerPanel.setLayout(new GridLayout(1, 1, 10, 10));
+        JTextArea textArea = new JTextArea(13, 80);
+        JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+        textArea.setEditable(false);
+        textArea.setText(licenseText);
+        centerPanel.add(scrollPane);
+
+        // radio buttons
+        JPanel southPanel = new JPanel();
+        RadioButtonListener radioButtonListener = new RadioButtonListener();
+        _acceptButton = new JRadioButton();
+        _acceptButton.setActionCommand(_ACCEPT_ACTION_COMMAND);
+        _acceptButton.addActionListener(radioButtonListener);
+        _doNotAcceptButton = new JRadioButton();
+        _doNotAcceptButton.setActionCommand(_DO_NOT_ACCEPT_ACTION_COMMAND);
+        _doNotAcceptButton.addActionListener(radioButtonListener);
+        ButtonGroup radioButtonGroup = new ButtonGroup();
+        radioButtonGroup.add(_acceptButton);
+        radioButtonGroup.add(_doNotAcceptButton);
+        JPanel radioPanel = new JPanel(new GridLayout(1, 0));
+        radioPanel.add(_acceptButton);
+        radioPanel.add(_doNotAcceptButton);
+        southPanel.add(radioPanel);
+
+        setLayout(new BorderLayout(0, 5));
+        add(centerPanel, BorderLayout.CENTER);
+        add(southPanel, BorderLayout.SOUTH);
+    }
+
+    protected String getTitle() {
+        return getText(LICENSE);
+    }
+
+    protected String getDescription() {
+        return getText(PLEASE_READ_LICENSE);
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return true;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        return _doNotAcceptButton;
+    }
+
+    protected void activate() {
+        _acceptButton.setText(getText(ACCEPT));
+        _doNotAcceptButton.setText(getText(DO_NOT_ACCEPT));
+        boolean accept = FrameInstaller.isAccept();
+        _acceptButton.setSelected(accept);
+        _doNotAcceptButton.setSelected(!accept);
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+    protected boolean isAccept() {
+        return _acceptButton.isSelected() && !_doNotAcceptButton.isSelected();
+    }
+
+    private final static class RadioButtonListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+            String actionCommand = e.getActionCommand();
+            if (actionCommand.equals(_ACCEPT_ACTION_COMMAND)) {
+                FrameInstaller.setAccept(true);
+            } else {
+                FrameInstaller.setAccept(false);
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/LicensePageValidator.java b/installer/src/java/org/python/util/install/LicensePageValidator.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/LicensePageValidator.java
@@ -0,0 +1,17 @@
+package org.python.util.install;
+
+public class LicensePageValidator extends AbstractWizardValidator {
+
+    LicensePage _page;
+
+    LicensePageValidator(LicensePage page) {
+        super();
+        _page = page;
+    }
+
+    protected void validate() throws ValidationException {
+        if (!_page.isAccept()) {
+            throw new ValidationException(getText(PLEASE_ACCEPT_LICENSE));
+        }
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/OverviewPage.java b/installer/src/java/org/python/util/install/OverviewPage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/OverviewPage.java
@@ -0,0 +1,243 @@
+package org.python.util.install;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.python.util.install.Installation.JavaVersionInfo;
+
+public class OverviewPage extends AbstractWizardPage {
+
+    private final static int _LONGER_LENGTH = 25;
+    private final static int _SHORTER_LENGTH = 10;
+
+    private JLabel _directoryLabel;
+    private JLabel _typeLabel;
+    private JTextField _directory;
+    private JTextField _type;
+    private JLabel _message;
+
+    private JLabel _osLabel;
+    private JCheckBox _osBox;
+    private JLabel _javaLabel;
+    private JTextField _javaVendor;
+    private JTextField _javaVersion;
+    private JCheckBox _javaBox;
+
+    public OverviewPage() {
+        super();
+        initComponents();
+    }
+
+    private void initComponents() {
+        _directoryLabel = new JLabel();
+        _directory = new JTextField(_LONGER_LENGTH);
+        _directory.setEditable(false);
+        _directory.setFocusable(false);
+        _typeLabel = new JLabel();
+        _type = new JTextField(_LONGER_LENGTH);
+        _type.setEditable(false);
+        _type.setFocusable(false);
+
+        _osLabel = new JLabel();
+        JTextField osName = new JTextField(_LONGER_LENGTH);
+        osName.setText(System.getProperty(Installation.OS_NAME));
+        osName.setToolTipText(System.getProperty(Installation.OS_NAME));
+        osName.setEditable(false);
+        osName.setFocusable(false);
+        JTextField osVersion = new JTextField(_SHORTER_LENGTH);
+        osVersion.setText(System.getProperty(Installation.OS_VERSION));
+        osVersion.setToolTipText(System.getProperty(Installation.OS_VERSION));
+        osVersion.setEditable(false);
+        osVersion.setFocusable(false);
+        _osBox = new JCheckBox();
+        _osBox.setEnabled(false);
+        _osBox.setSelected(Installation.isValidOs());
+        _osBox.setFocusable(false);
+
+        _javaLabel = new JLabel();
+        _javaLabel.setFocusable(false);
+        _javaVendor = new JTextField(_LONGER_LENGTH);
+        _javaVendor.setEditable(false);
+        _javaVendor.setFocusable(false);
+        _javaVersion = new JTextField(_SHORTER_LENGTH);
+        _javaVersion.setEditable(false);
+        _javaVersion.setFocusable(false);
+        _javaBox = new JCheckBox();
+        _javaBox.setEnabled(false);
+        _javaBox.setFocusable(false);
+
+        _message = new JLabel();
+
+        JPanel panel = new JPanel();
+        GridBagLayout gridBagLayout = new GridBagLayout();
+        panel.setLayout(gridBagLayout);
+        GridBagConstraints gridBagConstraints = newGridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        panel.add(_directoryLabel, gridBagConstraints);
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 0;
+        panel.add(_directory, gridBagConstraints);
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        panel.add(_typeLabel, gridBagConstraints);
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 1;
+        panel.add(_type, gridBagConstraints);
+
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        panel.add(_osLabel, gridBagConstraints);
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 2;
+        panel.add(osName, gridBagConstraints);
+        gridBagConstraints.gridx = 2;
+        gridBagConstraints.gridy = 2;
+        panel.add(osVersion, gridBagConstraints);
+        gridBagConstraints.gridx = 3;
+        gridBagConstraints.gridy = 2;
+        panel.add(_osBox, gridBagConstraints);
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 3;
+        panel.add(_javaLabel, gridBagConstraints);
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 3;
+        panel.add(_javaVendor, gridBagConstraints);
+        gridBagConstraints.gridx = 2;
+        gridBagConstraints.gridy = 3;
+        panel.add(_javaVersion, gridBagConstraints);
+        gridBagConstraints.gridx = 3;
+        gridBagConstraints.gridy = 3;
+        panel.add(_javaBox, gridBagConstraints);
+
+        // attn special constraints for message (should always be on last row)
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 4;
+        gridBagConstraints.gridwidth = 2;
+        panel.add(_message, gridBagConstraints);
+
+        add(panel);
+    }
+
+    protected String getTitle() {
+        return getText(OVERVIEW_TITLE);
+    }
+
+    protected String getDescription() {
+        return getText(OVERVIEW_DESCRIPTION);
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return true;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        return null;
+    }
+
+    protected void activate() {
+        // directory
+        _directoryLabel.setText(getText(TARGET_DIRECTORY_PROPERTY) + ": ");
+        _directory.setText(FrameInstaller.getTargetDirectory());
+        _directory.setToolTipText(FrameInstaller.getTargetDirectory());
+
+        // type
+        _typeLabel.setText(getText(INSTALLATION_TYPE) + ": ");
+        InstallationType installationType = FrameInstaller.getInstallationType();
+        String typeText;
+        if (installationType.isAll()) {
+            typeText = getText(ALL);
+        } else if (installationType.isStandard()) {
+            typeText = getText(STANDARD);
+        } else if (installationType.isMinimum()) {
+            typeText = getText(MINIMUM);
+        } else if (installationType.isStandalone()) {
+            typeText = getText(STANDALONE);
+        } else {
+            typeText = getText(CUSTOM);
+            typeText += " (";
+            boolean predecessor = false;
+            if (installationType.installLibraryModules()) {
+                if (predecessor) {
+                    typeText += " ";
+                }
+                typeText += InstallerCommandLine.INEXCLUDE_LIBRARY_MODULES;
+                predecessor = true;
+            }
+            if (installationType.installDemosAndExamples()) {
+                if (predecessor) {
+                    typeText += " ";
+                }
+                typeText += InstallerCommandLine.INEXCLUDE_DEMOS_AND_EXAMPLES;
+                predecessor = true;
+            }
+            if (installationType.installDocumentation()) {
+                if (predecessor) {
+                    typeText += " ";
+                }
+                typeText += InstallerCommandLine.INEXCLUDE_DOCUMENTATION;
+                predecessor = true;
+            }
+            if (installationType.installSources()) {
+                if (predecessor) {
+                    typeText += " ";
+                }
+                typeText += InstallerCommandLine.INEXCLUDE_SOURCES;
+                predecessor = true;
+            }
+            typeText += ")";
+        }
+        _type.setText(typeText);
+        _type.setToolTipText(typeText);
+
+        // os
+        _osLabel.setText(getText(OS_INFO) + ": ");
+        String osText;
+        if (_osBox.isSelected()) {
+            osText = getText(OK);
+        } else {
+            osText = getText(MAYBE_NOT_SUPPORTED);
+        }
+        _osBox.setText(osText);
+
+        // java
+        _javaLabel.setText(getText(JAVA_INFO) + ": ");
+        JavaVersionInfo javaVersionInfo = FrameInstaller.getJavaVersionInfo();
+        _javaVendor.setText(javaVersionInfo.getVendor());
+        _javaVendor.setToolTipText(javaVersionInfo.getVendor());
+        _javaVersion.setText(javaVersionInfo.getVersion());
+        _javaVersion.setToolTipText(javaVersionInfo.getVersion());
+        _javaBox.setSelected(Installation.isValidJava(javaVersionInfo));
+        String javaText;
+        if (_javaBox.isSelected()) {
+            javaText = getText(OK);
+        } else {
+            javaText = getText(NOT_OK);
+        }
+        _javaBox.setText(javaText);
+
+        // message
+        _message.setText(getText(CONFIRM_START, getText(NEXT)));
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ProgressListener.java b/installer/src/java/org/python/util/install/ProgressListener.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ProgressListener.java
@@ -0,0 +1,15 @@
+package org.python.util.install;
+
+public interface ProgressListener extends InstallationListener {
+
+    public int getInterval();
+
+    public void progressChanged(int newPercentage);
+
+    public void progressEntry(String entry);
+
+    public void progressStartScripts();
+
+    public void progressStandalone();
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ProgressPage.java b/installer/src/java/org/python/util/install/ProgressPage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ProgressPage.java
@@ -0,0 +1,122 @@
+package org.python.util.install;
+
+import java.awt.BorderLayout;
+import java.io.File;
+import java.io.IOException;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+
+import org.python.util.install.driver.Autotest;
+
+public class ProgressPage extends AbstractWizardPage implements ProgressListener {
+
+    private static final long serialVersionUID = 9013748834030994976L;
+
+    private JarInfo _jarInfo;
+    private JLabel _label;
+    private JProgressBar _progressBar;
+    private JLabel _progressEntry;
+    private Autotest _autotest;
+
+    public ProgressPage(JarInfo jarInfo, Autotest autotest) {
+        super();
+        _jarInfo = jarInfo;
+        _autotest = autotest;
+        initComponents();
+    }
+
+    private void initComponents() {
+        JPanel northPanel = new JPanel();
+        _label = new JLabel();
+        northPanel.add(_label);
+        _progressBar = new JProgressBar();
+        northPanel.add(_progressBar);
+        JPanel centerPanel = new JPanel();
+        _progressEntry = new JLabel();
+        centerPanel.add(_progressEntry);
+        setLayout(new BorderLayout(0, 5));
+        add(northPanel, BorderLayout.NORTH);
+        add(centerPanel, BorderLayout.CENTER);
+    }
+
+    protected String getTitle() {
+        return getText(INSTALLATION_IN_PROGRESS);
+    }
+
+    protected String getDescription() {
+        return getText(PLEASE_WAIT);
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return false;
+    }
+
+    protected boolean isNextVisible() {
+        return false;
+    }
+
+    protected JComponent getFocusField() {
+        return null;
+    }
+
+    protected void activate() {
+        _label.setText(getText(PROGRESS) + ": ");
+        _progressBar.setValue(0);
+        _progressBar.setStringPainted(true);
+        try {
+            _progressEntry.setText(getText(INFLATING, _jarInfo.getJarFile().getName()));
+        } catch (IOException e) {
+            // should not happen
+        }
+        JarInstaller jarInstaller = new JarInstaller(this, _jarInfo);
+        if (_autotest != null) {
+            jarInstaller.addInstallationListener(_autotest);
+        }
+        File targetDirectory = new File(FrameInstaller.getTargetDirectory());
+        JavaHomeHandler javaHomeHandler = FrameInstaller.getJavaHomeHandler();
+        jarInstaller.inflate(targetDirectory, FrameInstaller.getInstallationType(), javaHomeHandler);
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+    //
+    // interface ProgressListener
+    //
+
+    public void progressChanged(int newPercentage) {
+        _progressBar.setValue(newPercentage);
+    }
+
+    public int getInterval() {
+        return 1;
+    }
+
+    public void progressFinished() {
+        _progressBar.setValue(100);
+        getWizard().gotoNextPage();
+    }
+
+    public void progressEntry(String entry) {
+        _progressEntry.setText(getText(INFLATING, entry));
+    }
+
+    public void progressStartScripts() {
+        _progressEntry.setText(getText(GENERATING_START_SCRIPTS));
+    }
+
+    public void progressStandalone() {
+        _progressEntry.setText(getText(PACKING_STANDALONE_JAR));
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ReadmePage.java b/installer/src/java/org/python/util/install/ReadmePage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ReadmePage.java
@@ -0,0 +1,75 @@
+package org.python.util.install;
+
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.io.IOException;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+public class ReadmePage extends AbstractWizardPage {
+
+    private JTextArea _textArea;
+    private JarInfo _jarInfo;
+
+    public ReadmePage(JarInfo jarInfo) {
+        super();
+        _jarInfo = jarInfo;
+        initComponents();
+    }
+
+    private void initComponents() {
+        JPanel centerPanel = new JPanel();
+        centerPanel.setLayout(new GridLayout(1, 1));
+        _textArea = new JTextArea(13, 80);
+        JScrollPane scrollPane = new JScrollPane(_textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+        _textArea.setEditable(false);
+        _textArea.setText("n/a");
+        centerPanel.add(scrollPane);
+
+        setLayout(new BorderLayout(0, 5));
+        add(centerPanel, BorderLayout.CENTER);
+    }
+
+    protected String getTitle() {
+        return getText(README);
+    }
+
+    protected String getDescription() {
+        return getText(PLEASE_README);
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return false;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        return null;
+    }
+
+    protected void activate() {
+        try {
+            _textArea.setText(_jarInfo.getReadmeText());
+        } catch (IOException ioe) {
+            throw new InstallerException(ioe);
+        }
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/StandalonePackager.java b/installer/src/java/org/python/util/install/StandalonePackager.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/StandalonePackager.java
@@ -0,0 +1,184 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+public class StandalonePackager {
+
+    private final static int BUF_SIZE = 1024;
+
+    private File _jarFile;
+    private Manifest _manifest;
+    private JarOutputStream _jarOut;
+
+    /**
+     * Helper class to pack stuff into a single .jar file.
+     * <p>
+     * If a .jar file has to be added, this should be the first add (because of the MANIFEST).
+     */
+    public StandalonePackager(File jarFile) {
+        _jarFile = jarFile;
+    }
+
+    /**
+     * add a file, in given parent dir (<code>null</code> = top)
+     * 
+     * @param file to write to jar
+     * @param parentDir as String
+     * 
+     * @throws IOException
+     */
+    public void addFile(File file, String parentDir) throws IOException {
+        byte[] buffer = new byte[BUF_SIZE];
+        FileInputStream inputStream = null;
+        try {
+            inputStream = new FileInputStream(file);
+            String jarEntryName = null;
+            if (parentDir != null && parentDir.length() > 0) {
+                jarEntryName = parentDir + "/" + file.getName();
+            } else {
+                jarEntryName = file.getName();
+            }
+            getJarOutputStream().putNextEntry(new JarEntry(jarEntryName));
+            for (int read = 0; read != -1; read = inputStream.read(buffer)) {
+                getJarOutputStream().write(buffer, 0, read);
+            }
+            getJarOutputStream().closeEntry();
+        } finally {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        }
+    }
+
+    /**
+     * add a full directory
+     * 
+     * @param directory
+     * @throws IOException
+     */
+    public void addFullDirectory(File directory) throws IOException {
+        addDirectory(directory, null);
+    }
+
+    /**
+     * add the contents of a given jar file
+     * 
+     * @param jarFile to add
+     * @throws IOException
+     * @throws FileNotFoundException
+     */
+    public void addJarFile(File jarFile) throws FileNotFoundException, IOException {
+        JarFile jarJarFile = new JarFile(jarFile);
+        try {
+            _manifest = jarJarFile.getManifest();
+        } finally {
+            jarJarFile.close();
+        }
+
+        JarInputStream inputStr = null;
+        try {
+            inputStr = new JarInputStream(new FileInputStream(jarFile));
+            JarEntry entry = inputStr.getNextJarEntry();
+            while (entry != null) {
+                getJarOutputStream().putNextEntry(entry);
+                byte[] buffer = new byte[BUF_SIZE];
+                int len;
+                while ((len = inputStr.read(buffer)) > 0) {
+                    getJarOutputStream().write(buffer, 0, len);
+                }
+                getJarOutputStream().closeEntry();
+                entry = inputStr.getNextJarEntry();
+            }
+        } finally {
+            if (inputStr != null) {
+                try {
+                    inputStr.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    public void close() throws FileNotFoundException, IOException {
+        getJarOutputStream().close();
+    }
+
+    /**
+     * removes all files in directory dir (and subdirectories), except excludeFile.
+     * 
+     * @param dir
+     * @param excludeFile
+     */
+    public static void emptyDirectory(File dir, File excludeFile) {
+        File[] files = dir.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            if (!files[i].equals(excludeFile)) {
+                if (files[i].isDirectory()) {
+                    emptyDirectory(files[i], excludeFile);
+                } else {
+                    files[i].delete();
+                }
+            }
+        }
+        if (dir.listFiles().length == 0) {
+            dir.delete();
+        }
+    }
+
+    /**
+     * add the given directory to the jar, including all subdirectories, in given parent dir (<code>null</code> =
+     * top)
+     * 
+     * @param dir to add to the jar
+     * @param parentDir to save in jar
+     * @throws IOException
+     */
+    private void addDirectory(File dir, String parentDir) throws IOException {
+        if (!dir.isDirectory())
+            return;
+        File[] filesInDir = dir.listFiles();
+        for (int i = 0; i < filesInDir.length; i++) {
+            File currentFile = filesInDir[i];
+            if (currentFile.isFile()) {
+                if (parentDir != null && parentDir.length() > 0) {
+                    addFile(currentFile, parentDir + "/" + dir.getName());
+                } else {
+                    addFile(currentFile, dir.getName());
+                }
+            } else {
+                String newParentDir = null;
+                if (parentDir != null && parentDir.length() > 0) {
+                    newParentDir = parentDir + "/" + dir.getName();
+                } else {
+                    newParentDir = dir.getName();
+                }
+                addDirectory(currentFile, newParentDir);
+            }
+        }
+    }
+
+    /**
+     * @return the jar output stream
+     */
+    private JarOutputStream getJarOutputStream() throws FileNotFoundException, IOException {
+        if (_jarOut == null) {
+            if (_manifest != null) {
+                _jarOut = new JarOutputStream(new FileOutputStream(_jarFile), _manifest);
+            } else {
+                _jarOut = new JarOutputStream(new FileOutputStream(_jarFile));
+            }
+        }
+        return _jarOut;
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/StartScriptGenerator.java b/installer/src/java/org/python/util/install/StartScriptGenerator.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/StartScriptGenerator.java
@@ -0,0 +1,244 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Date;
+
+public class StartScriptGenerator {
+
+    protected final static int UNIX_FLAVOUR = 10;
+
+    protected final static int WINDOWS_FLAVOUR = 30;
+
+    protected final static int BOTH_FLAVOUR = 50;
+
+    protected final static String WIN_CR_LF;
+
+    private final static String JAVA_HOME = "JAVA_HOME";
+
+    /** do not hard-wire JYTHON_HOME */
+    private final static String JYTHON_HOME_FALLBACK = "JYTHON_HOME_FALLBACK";
+
+    private final static String JYTHON = "jython";
+
+    private final static String JYTHON_BAT = "jython.bat";
+    static {
+        int dInt = Integer.parseInt("0d", 16);
+        int aInt = Integer.parseInt("0a", 16);
+        WIN_CR_LF = new String(new char[] {(char)dInt, (char)aInt});
+    }
+
+    private File _targetDirectory;
+
+    private JavaHomeHandler _javaHomeHandler;
+
+    private int _flavour;
+
+    public StartScriptGenerator(File targetDirectory, JavaHomeHandler javaHomeHandler) {
+        _targetDirectory = targetDirectory;
+        _javaHomeHandler = javaHomeHandler;
+        if (Installation.isWindows()) {
+            setFlavour(WINDOWS_FLAVOUR);
+        } else {
+            // everything else defaults to unix at the moment
+            setFlavour(UNIX_FLAVOUR);
+        }
+    }
+
+    protected void setFlavour(int flavour) {
+        _flavour = flavour;
+        if (flavour == WINDOWS_FLAVOUR) {
+            // check if we should create unix like scripts, too
+            if (hasUnixlikeShell()) {
+                _flavour = BOTH_FLAVOUR;
+            }
+        }
+    }
+
+    protected int getFlavour() {
+        return _flavour;
+    }
+
+    protected boolean hasUnixlikeShell() {
+        int errorCode = 0;
+        try {
+            String command[] = new String[] {"sh", "-c", "env"};
+            long timeout = 3000;
+            ChildProcess childProcess = new ChildProcess(command, timeout);
+            childProcess.setDebug(false);
+            childProcess.setSilent(true);
+            errorCode = childProcess.run();
+        } catch (Throwable t) {
+            errorCode = 1;
+        }
+        return errorCode == 0;
+    }
+
+    protected final void generateStartScripts() throws IOException {
+        File bin = new File(getTargetDirectory(), "bin");
+        File bin_jython = new File(bin, JYTHON);
+        switch(getFlavour()){
+            case BOTH_FLAVOUR:
+                writeToTargetDir(JYTHON_BAT, getJythonScript(WINDOWS_FLAVOUR));
+                FileHelper.makeExecutable(writeToTargetDir(JYTHON, getJythonScript(BOTH_FLAVOUR)));
+                FileHelper.makeExecutable(bin_jython);
+                break;
+            case WINDOWS_FLAVOUR:
+                writeToTargetDir(JYTHON_BAT, getJythonScript(WINDOWS_FLAVOUR));
+                // delete the *nix script in /bin dir
+                bin_jython.delete();
+                break;
+            default:
+                FileHelper.makeExecutable(writeToTargetDir(JYTHON, getJythonScript(UNIX_FLAVOUR)));
+                FileHelper.makeExecutable(bin_jython);
+                // delete the windows script in /bin dir
+                File bin_jython_bat = new File(bin, JYTHON_BAT);
+                bin_jython_bat.delete();
+                break;
+        }
+    }
+
+    /**
+     * only <code>protected</code> for unit test use
+     */
+    protected final String getJythonScript(int flavour) throws IOException {
+        if (flavour == WINDOWS_FLAVOUR) {
+            return getStartScript(getWindowsJythonTemplate()) + readFromFile(JYTHON_BAT);
+        } else {
+            return getStartScript(getUnixJythonTemplate()) + readFromFile(JYTHON);
+        }
+    }
+
+    /**
+     * These placeholders are valid for all private methods:
+     * 
+     * {0} : current date <br>
+     * {1} : user.name <br>
+     * {2} : target directory <br>
+     */
+    private String getStartScript(String template) throws IOException {
+        String parameters[] = new String[4];
+        parameters[0] = new Date().toString();
+        parameters[1] = System.getProperty("user.name");
+        parameters[2] = getTargetDirectory().getCanonicalPath();
+        return MessageFormat.format(template, (Object[])parameters);
+    }
+
+    /**
+     * placeholders:
+     * 
+     * @see getStartScript
+     */
+    private String getWindowsJythonTemplate() {
+        StringBuilder builder = getWindowsHeaderTemplate();
+        builder.append("set ");
+        builder.append(JAVA_HOME);
+        builder.append("=");
+        if (_javaHomeHandler.isValidHome()) {
+            builder.append("\"");
+            builder.append(_javaHomeHandler.getHome().getAbsolutePath());
+            builder.append("\"");
+        }
+        builder.append(WIN_CR_LF);
+        builder.append("set ");
+        builder.append(JYTHON_HOME_FALLBACK);
+        builder.append("=\"{2}\"");
+        builder.append(WIN_CR_LF);
+        builder.append(WIN_CR_LF);
+        return builder.toString();
+    }
+
+    /**
+     * placeholders:
+     * 
+     * @see getStartScript
+     */
+    private StringBuilder getWindowsHeaderTemplate() {
+        StringBuilder builder = new StringBuilder(1000);
+        builder.append("@echo off");
+        builder.append(WIN_CR_LF);
+        builder.append("rem Prevent leak of environment variables");
+        builder.append(WIN_CR_LF);
+        builder.append("setlocal");
+        builder.append(WIN_CR_LF);
+        builder.append("rem This file was generated by the Jython installer");
+        builder.append(WIN_CR_LF);
+        builder.append("rem Created on {0} by {1}");
+        builder.append(WIN_CR_LF);
+        builder.append(WIN_CR_LF);
+        return builder;
+    }
+
+    /**
+     * placeholders:
+     * 
+     * @see getStartScript
+     */
+    private String getUnixJythonTemplate() {
+        StringBuilder builder = getUnixHeaderTemplate();
+        builder.append(JAVA_HOME);
+        builder.append("=");
+        if (_javaHomeHandler.isValidHome()) {
+            builder.append("\"");
+            builder.append(_javaHomeHandler.getHome().getAbsolutePath());
+            builder.append("\"");
+        }
+        builder.append("\n");
+        builder.append(JYTHON_HOME_FALLBACK);
+        builder.append("=\"{2}\"\n");
+        builder.append("\n");
+        return builder.toString();
+    }
+
+    /**
+     * placeholders:
+     * 
+     * @see getStartScript
+     */
+    private StringBuilder getUnixHeaderTemplate() {
+        StringBuilder builder = new StringBuilder(1000);
+        builder.append("#!/usr/bin/env bash\n");
+        builder.append("\n");
+        builder.append("# This file was generated by the Jython installer\n");
+        builder.append("# Created on {0} by {1}\n");
+        builder.append("\n");
+        return builder;
+    }
+
+    /**
+     * @param fileName
+     *            The short file name, e.g. JYTHON_BAT
+     * 
+     * @throws IOException
+     */
+    private String readFromFile(String fileName) throws IOException {
+        // default runtime location
+        File targetDirectory = getTargetDirectory();
+        File file = new File(new File(targetDirectory, "bin"), fileName);
+        if (!file.exists()) {
+            // deviation: test time location
+            file = new File(targetDirectory, fileName);
+        }
+        return FileHelper.readAll(file);
+    }
+
+    /**
+     * Create (or overwrite) the specified file in the target directory
+     * 
+     * @param fileName
+     *            The short file name, e.g. JYTHON_BAT
+     * @param contents
+     * 
+     * @throws IOException
+     */
+    private File writeToTargetDir(String fileName, String contents) throws IOException {
+        File file = new File(getTargetDirectory(), fileName);
+        FileHelper.write(file, contents);
+        return file;
+    }
+
+    private File getTargetDirectory() {
+        return _targetDirectory;
+    }
+}
diff --git a/installer/src/java/org/python/util/install/SuccessPage.java b/installer/src/java/org/python/util/install/SuccessPage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/SuccessPage.java
@@ -0,0 +1,55 @@
+package org.python.util.install;
+
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+
+public class SuccessPage extends AbstractWizardPage {
+
+    private JarInfo _jarInfo;
+    private JLabel _label;
+
+    public SuccessPage(JarInfo jarInfo) {
+        super();
+        _jarInfo = jarInfo;
+        initComponents();
+    }
+
+    private void initComponents() {
+        _label = new JLabel();
+        add(_label);
+    }
+
+    protected String getTitle() {
+        return getText(CONGRATULATIONS);
+    }
+
+    protected String getDescription() {
+        return getText(SUCCESS, _jarInfo.getVersion(), FrameInstaller.getTargetDirectory());
+    }
+
+    protected boolean isCancelVisible() {
+        return false;
+    }
+
+    protected boolean isPreviousVisible() {
+        return false;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        return null;
+    }
+
+    protected void activate() {
+        _label.setText(getText(PRESS_FINISH, getWizard().getFinishString()));
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/TextConstants.java b/installer/src/java/org/python/util/install/TextConstants.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/TextConstants.java
@@ -0,0 +1,148 @@
+package org.python.util.install;
+
+import java.util.ListResourceBundle;
+
+public class TextConstants extends ListResourceBundle implements TextKeys {
+
+    static final Object[][] contents = {
+        // LOCALIZE THIS
+
+        { ACCEPT, "I accept" }, // license
+        { ALL, "All (everything, including sources)" }, // installation type
+        { BROWSE, "Browse..." }, // button (open the JFileChooser)
+        { CANCEL, "Cancel" }, // button
+        { CHOOSE_LOCATION, "Choose the location where you want Jython to be installed to" }, // selection
+        { CHOOSE_JRE, "Choose the java version (JRE/JDK) to run Jython with" }, // selection
+        { CONFIRM_START, "Please press {0} to start the installation" }, // overview
+        { CONGRATULATIONS, "Congratulations!" }, // congratulations
+        { CORE, "Core" }, // installation type
+        { CREATED_DIRECTORY, "Created directory {0}" }, // directory
+        { CURRENT, "Current" }, // directory
+        { CUSTOM, "Custom" }, // installation type
+        { DEMOS_EXAMPLES, "Demos and examples" }, // installation type
+        { DO_NOT_ACCEPT, "I do not accept" }, // license
+        { DIRECTORIES_ONLY, "Directories only" }, // file chooser
+        { DOCUMENTATION, "Documentation" }, // installation type
+        { EMPTY_TARGET_DIRECTORY, "Target directory must not be empty" }, // error
+        { ENGLISH, "English" }, // language
+        { ERROR, "Error" }, // error
+        { ERROR_ACCESS_JARFILE, "Error accessing jar file" }, // error
+        { FINISH, "Finish" }, // button
+        { GENERATING_START_SCRIPTS, "Generating start scripts ..." }, // progress
+        { GERMAN, "German" }, // language
+        { INFLATING, "Inflating {0}" }, // progress
+        { INFORMATION, "Information" }, // information
+        { INSTALLATION_CANCELLED, "Installation cancelled." }, // final
+        { INSTALLATION_IN_PROGRESS, "The installation is now in progress" }, // progress
+        { INSTALLATION_TYPE_DESCRIPTION, "The following installation types are available" }, // installation type
+        { INSTALLATION_TYPE, "Installation type" }, // installation type
+        { JAR_NOT_FOUND, "Unable to find jar file {0}." }, // error
+        { JAVA_INFO, "Java vendor / version" }, // version
+        { JYTHON_INSTALL, "Jython Installation" }, // title
+        { LANGUAGE_PROPERTY, "Language" }, // language
+        { LIBRARY_MODULES, "Library modules" }, // installation type
+        { LICENSE, "License agreement" }, // license
+        { MAYBE_NOT_SUPPORTED, "Maybe not supported" }, // version
+        { MINIMUM, "Minimum (core)" }, // installation type
+        { NEXT, "Next" }, // button
+        { NON_EMPTY_TARGET_DIRECTORY, "Target directory is not empty" }, // error
+        { NO_MANIFEST, "No manifest found in jar file {0}." }, // error
+        { NOT_OK, "Not ok !" }, // version
+        { OK, "Ok" }, // version
+        { OS_INFO, "OS name / version" }, // version
+        { OTHER, "Other" }, // directory
+        { OVERVIEW_DESCRIPTION, "The installation will be done using the following options" }, // overview
+        { OVERVIEW_TITLE, "Overview (summary of options)" }, // overview
+        { PACKING_STANDALONE_JAR, "Packing standalone " + JarInstaller.JYTHON_JAR + " ..." }, // progress
+        { PLEASE_ACCEPT_LICENSE, "Please read and accept the license agreement" }, // license
+        { PLEASE_README, "Please read the following information" }, // readme
+        { PLEASE_READ_LICENSE, "Please read the license agreement carefully" }, // license
+        { PLEASE_WAIT, "Please stand by, this may take a few seconds ..." }, // progress
+        { PRESS_FINISH, "Please press {0} to exit the installation." }, // finish
+        { PREVIOUS, "Previous" }, // button
+        { PROGRESS, "Progress" }, // progress
+        { README, "README" }, // readme
+        { SELECT, "Select" }, // button (approval in JFileChooser)
+        { SELECT_INSTALLATION_TYPE, "Please select the installation type" }, // installation type
+        { SELECT_JAVA_HOME, "Please select the java home directory" }, // directory
+        { SELECT_LANGUAGE, "Please select your language" }, // language
+        { SELECT_TARGET_DIRECTORY, "Please select the target directory" }, // directory
+        { SOURCES, "Sources" }, // installation type
+        { STANDARD, "Standard (core, library modules, demos, examples, documentation)" }, // installation type
+        { STANDALONE, "Standalone (a callable .jar file)" }, // installation type
+        { SUCCESS, "You successfully installed Jython {0} to directory {1}." }, // success
+        { TARGET_DIRECTORY_PROPERTY, "Target directory" }, // property as title
+        { TARGET_JAVA_HOME_PROPERTY, "Target java home" }, // property as title
+        { UNABLE_CREATE_DIRECTORY, "Unable to create directory {0}." }, // error
+        { UNABLE_CREATE_FILE, "Unable to create file {0}." }, // error
+        { UNABLE_TO_DELETE, "Unable to delete {0}" }, // error
+        { UNEXPECTED_URL, "Unexpected URL {0} found for installation jar file." }, // error
+        { VERSION_INFO, "You are about to install Jython version {0}" }, // version
+        { WELCOME_TO_JYTHON, "Welcome to Jython !" }, // welcome
+        { ZIP_ENTRY_SIZE, "Size of zip entry {0} unknown." }, // error
+        { ZIP_ENTRY_TOO_BIG, "Zip entry {0} too big." }, // error
+
+        // console texts (C_*) should not contain special characters (like e.g. ü)
+        { C_ACCEPT, "Do you accept the license agreement ?" }, // license
+        { C_ALL, "All (everything, including sources)" }, // installation type
+        { C_AT_ANY_TIME_CANCEL, "(at any time, answer {0} to cancel the installation)" }, // console
+        { C_AVAILABLE_LANGUAGES, "For the installation process, the following languages are available: {0}" }, // console
+        { C_CHECK_JAVA_VERSION, "Checking java version ..." }, // progress
+        { C_CLEAR_DIRECTORY, "Contents of directory {0} will be deleted now! Are you sure to proceed ?" }, //console
+        { C_CONFIRM_TARGET, "Please confirm copying of files to directory {0}" }, // console
+        { C_CONGRATULATIONS, "Congratulations!" }, // congratulations
+        { C_CREATE_DIRECTORY, "Unable to find directory {0}, create it ?" }, // console
+        { C_ENTER_TARGET_DIRECTORY, "Please enter the target directory" }, // console
+        { C_ENTER_JAVA_HOME, "Please enter the java home directory (empty for using the current java runtime)" }, // console
+        { C_ENGLISH, "English" }, // language
+        { C_EXCLUDE, "Do you want to exclude parts from the installation ?" }, // installation type
+        { C_GENERATING_START_SCRIPTS, "Generating start scripts ..." }, // progress
+        { C_GERMAN, "German" }, // language
+        { C_INCLUDE, "Do you want to install additional parts ?" }, // installation type
+        { C_INEXCLUDE_PARTS, "The following parts are selectable ({0} = no more)" }, // installation type
+        { C_INSTALL_TYPES, "The following installation types are available:" }, // installation type
+        { C_INVALID_ANSWER, "Answer {0} is not valid here" }, // error
+        { C_JAVA_VERSION, "Your java version to start Jython is: {0} / {1}" }, // version
+        { C_MINIMUM, "Minimum (core)" }, // installation type
+        { C_NO, "n" }, // answer
+        { C_NO_BIN_DIRECTORY, "There is no /bin directory below {0}." }, // error
+        { C_NO_JAVA_EXECUTABLE, "No java executable found in {0}." }, // error
+        { C_NO_VALID_JAVA, "No valid java found in {0}." }, // error
+        { C_NON_EMPTY_TARGET_DIRECTORY, "Target directory {0} is not empty" }, // error
+        { C_NOT_A_DIRECTORY, "{0} is not a directory. " }, // error
+        { C_NOT_FOUND, "{0} not found. " }, // error
+        { C_OS_VERSION, "Your operating system version is: {0} / {1}" }, // version
+        { C_OVERWRITE_DIRECTORY, "Directory {0} is not empty - ok to overwrite contents ?" }, // console
+        { C_PACKING_STANDALONE_JAR, "Packing standalone " + JarInstaller.JYTHON_JAR + " ..." }, // progress
+        { C_PROCEED, "Please press Enter to proceed" }, // console
+        { C_PROCEED_ANYWAY, "Please press Enter to proceed anyway" }, // console
+        { C_READ_LICENSE, "Do you want to read the license agreement now ?" }, // license
+        { C_READ_README, "Do you want to show the contents of README ?" }, // readme
+        { C_SCHEDULED, "{0} scheduled for installation" }, // installation type
+        { C_SELECT_INSTALL_TYPE, "Please select the installation type" }, // installation type
+        { C_SELECT_LANGUAGE, "Please select your language" }, // language
+        { C_SILENT_INSTALLATION, "Performing silent installation" }, // installation mode
+        { C_STANDALONE, "Standalone (a single, executable .jar)" }, //installation mode
+        { C_STANDARD, "Standard (core, library modules, demos and examples, documentation)" }, // installation type
+        { C_SUCCESS, "You successfully installed Jython {0} to directory {1}." }, // success
+        { C_SUMMARY, "Summary:" }, // summary
+        { C_TO_CURRENT_JAVA, "Warning: switching back to current JDK due to error: {0}." }, // warning
+        { C_UNABLE_CREATE_DIRECTORY, "Unable to create directory {0}." }, // error
+        { C_UNABLE_CREATE_TMPFILE, "Unable to create temp file {0}." }, // error
+        { C_UNSCHEDULED, "{0} excluded from installation" }, // installation type
+        { C_UNABLE_TO_DELETE, "Unable to delete {0}" }, // error
+        { C_UNSUPPORTED_JAVA, "This java version is not supported." }, // version
+        { C_UNSUPPORTED_OS, "This operating system might not be fully supported." }, // version
+        { C_USING_TYPE, "Using installation type {0}" }, // installation type
+        { C_VERSION_INFO, "You are about to install Jython version {0}" }, // version
+        { C_WELCOME_TO_JYTHON, "Welcome to Jython !" }, // welcome
+        { C_YES, "y" }, // answer
+        
+        // END OF MATERIAL TO LOCALIZE
+    };
+
+    public Object[][] getContents() {
+        return contents;
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/TextConstants_de.java b/installer/src/java/org/python/util/install/TextConstants_de.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/TextConstants_de.java
@@ -0,0 +1,148 @@
+package org.python.util.install;
+
+import java.util.ListResourceBundle;
+
+public class TextConstants_de extends ListResourceBundle implements TextKeys, UnicodeSequences {
+
+    static final Object[][] contents = {
+        // Die folgenden Texte duerfen Umlaute und Sonderzeichen enthalten, aber nur als Unicode Escape Sequenzen aus UnicodeSequences
+        // The following texts may contain special characters, but only as unicode escape sequences from UnicodeSequences
+        { ACCEPT, "Ja, ich akzeptiere" }, // license
+        { ALL, "Alles (volle Installation, inklusive Quellcode)" }, // installation type
+        { BROWSE, "Suchen..." }, // button (open the JFileChooser)
+        { CANCEL, "Abbrechen" }, // button text
+        { CHOOSE_LOCATION, "W"+a2+"hlen Sie das Verzeichnis, in das Jython installiert werden soll" }, // selection
+        { CHOOSE_JRE, "Bestimmen Sie die Java Version (JRE/JDK), mit welcher Jython gestartet werden soll" }, // selection
+        { CONFIRM_START, "Bitte dr"+u2+"cken Sie {0}, um die Installation zu starten" }, // overview
+        { CONGRATULATIONS, "Gratulation!" }, // congratulations
+        { CORE, "Kern" }, // installation type
+        { CREATED_DIRECTORY, "Verzeichnis {0} wurde erstellt" }, // directory
+        { CURRENT, "Das aktuelle" }, // directory
+        { CUSTOM, "Benutzerdefiniert" }, // installation type
+        { DEMOS_EXAMPLES, "Demos und Beispiele" }, // installation type
+        { DIRECTORIES_ONLY, "Nur Verzeichnisse" }, // file chooser
+        { DOCUMENTATION, "Dokumentation" }, // installation type
+        { DO_NOT_ACCEPT, "Nein, ich akzeptiere nicht" }, // license
+        { EMPTY_TARGET_DIRECTORY, "Das Zielverzeichnis darf nicht leer sein" }, // error
+        { ENGLISH, "Englisch" }, // language
+        { ERROR, "Fehler" }, // error
+        { ERROR_ACCESS_JARFILE, "Problem beim Zugriff auf das jar File" }, // error
+        { FINISH, "Beenden" }, // button
+        { GENERATING_START_SCRIPTS, "Start Scripts werden generiert ..." }, // progress
+        { GERMAN, "Deutsch" }, // language
+        { INFLATING, "Entpacke {0}" }, // progress
+        { INFORMATION, "Information" }, // information
+        { INSTALLATION_CANCELLED, "Sie haben die Installation abgebrochen." }, // final
+        { INSTALLATION_IN_PROGRESS, "Die Installation l"+a2+"uft" }, // progress
+        { INSTALLATION_TYPE_DESCRIPTION, "Die folgenden Installationstypen sind verf"+u2+"gbar" }, // installation type
+        { INSTALLATION_TYPE, "Installationstyp" }, // installation type
+        { JAVA_INFO, "Java Hersteller / Version" }, // version
+        { JAR_NOT_FOUND, "Jar File {0} nicht gefunden." }, // error
+        { JYTHON_INSTALL, "Jython Installation" }, // title
+        { LANGUAGE_PROPERTY, "Sprache" }, // language
+        { LIBRARY_MODULES, "Bibliotheksmodule" }, // installation type
+        { LICENSE, "Lizenzvereinbarung" }, // license
+        { MAYBE_NOT_SUPPORTED, "Eventuell nicht unterst"+u2+"tzt" }, // version
+        { MINIMUM, "Minimum (Kern)" }, // installation type
+        { NEXT, "Weiter" }, // button
+        { NON_EMPTY_TARGET_DIRECTORY, "Das Zielverzeichnis enth"+a2+"lt bereits Daten." }, // error
+        { NO_MANIFEST, "Jar File {0} enth"+a2+"lt kein Manifest." }, // error
+        { NOT_OK, "Nicht ok !" }, // version
+        { OK, "Ok" }, // version
+        { OS_INFO, "Betriebssystem / Version" }, // version
+        { OTHER, "Ein abweichendes" }, // directory
+        { OVERVIEW_DESCRIPTION, "Sie haben folgende Einstellungen f"+u2+"r die Installation ausgew"+a2+"hlt" }, // overview
+        { OVERVIEW_TITLE, U2+"bersicht "+u2+"ber die gew"+a2+"hlten Einstellungen" }, // overview
+        { PACKING_STANDALONE_JAR, "Das standalone " + JarInstaller.JYTHON_JAR + " File wird erstellt ..." }, // progress
+        { PLEASE_ACCEPT_LICENSE, "Bitte lesen und akzeptieren Sie die Lizenzvereinbarung" }, // license
+        { PLEASE_README, "Bitte lesen Sie die folgenden Informationen" }, // readme
+        { PLEASE_READ_LICENSE, "Bitte lesen Sie die Lizenzvereinbarung sorf"+a2+"ltig durch" }, // license
+        { PLEASE_WAIT, "Bitte um etwas Geduld, die Installation kann einige Sekunden dauern ..." }, // progress
+        { PRESS_FINISH, "Bitte dr"+u2+"cken Sie {0}, um die Installation abzuschliessen." }, // finish
+        { PREVIOUS, "Zur"+u2+"ck" }, // button
+        { PROGRESS, "Fortschritt" }, // progress
+        { README, "README" }, // readme
+        { SELECT, "Ausw"+a2+"hlen" }, // button (approval in JFileChooser)
+        { SELECT_INSTALLATION_TYPE, "Bitte w"+a2+"hlen Sie den Installationstyp" }, // installation type
+        { SELECT_JAVA_HOME, "Bitte w"+a2+"hlen Sie das Java Home Verzeichnis" }, // directory
+        { SELECT_LANGUAGE, "Bitte w"+a2+"hlen Sie Ihre Sprache" }, // language
+        { SELECT_TARGET_DIRECTORY, "Bitte w"+a2+"hlen Sie das Zielverzeichnis" }, // directory
+        { SOURCES, "Quellcode" }, // installation type
+        { STANDARD, "Standard (Kern, Bibliotheksmodule, Demos, Beispiele, Dokumentation)" }, // installation type
+        { STANDALONE, "Standalone (ein ausf"+u2+"hrbares .jar File)" }, // installation type
+        { SUCCESS, "Sie haben Jython {0} erfolgreich im Verzeichnis {1} installiert." }, // final
+        { TARGET_DIRECTORY_PROPERTY, "Zielverzeichnis" }, // property als Titel
+        { TARGET_JAVA_HOME_PROPERTY, "Java Home Verzeichnis" }, // property als Titel
+        { UNABLE_CREATE_DIRECTORY, "Fehler beim Erstellen von Verzeichnis {0}." }, // error
+        { UNABLE_CREATE_FILE, "Fehler beim Erstellen von File {0}." }, // error
+        { UNABLE_TO_DELETE, "Fehler beim L"+o2+"schen von {0}" }, // console
+        { UNEXPECTED_URL, "Das Jar File f"+u2+"r die Installation weist eine unerwartete URL {0} auf." }, // error
+        { VERSION_INFO, "Sie sind im Begriff, Jython Version {0} zu installieren." }, // version
+        { WELCOME_TO_JYTHON, "Willkommen bei Jython !" }, // welcome
+        { ZIP_ENTRY_SIZE, "Der Zip Eintrag {0} hat eine unbekannte Gr"+o2+"sse." }, // error
+        { ZIP_ENTRY_TOO_BIG, "Der Zip Eintrag {0} ist zu gross." }, // error
+
+        // Konsole Texte (beginnend mit C_) duerfen keine Umlaute und andere Sonderzeichen enthalten:
+        // console texts (beginning with C_) must not contain special characters (use ASCII only):
+        { C_ACCEPT, "Akzeptieren Sie die Lizenzvereinbarung ?" }, // license
+        { C_ALL, "Alles (volle Installation, inklusive Quellcode)" }, // installation type
+        { C_AT_ANY_TIME_CANCEL, "(Sie koennen die Installation jederzeit durch Eingabe von {0} abbrechen)" }, // console
+        { C_AVAILABLE_LANGUAGES, "Die folgenden Sprachen sind fuer den Installationsvorgang verfuegbar: {0}" }, // languages
+        { C_CHECK_JAVA_VERSION, "Ueberpruefung der Java Version ..." }, // progress
+        { C_CLEAR_DIRECTORY, "Der Inhalt von Verzeichnis {0} wird anschliessend geloescht! Moechten Sie wirklich weiterfahren ?" }, //console
+        { C_CONFIRM_TARGET, "Bitte bestaetigen Sie den Start des Kopiervorgangs ins Verzeichnis {0}" }, // console
+        { C_CONGRATULATIONS, "Gratulation!" }, // congratulations
+        { C_CREATE_DIRECTORY, "Das Verzeichnis {0} gibt es nicht - soll es erstellt werden ?" }, // console
+        { C_ENTER_TARGET_DIRECTORY, "Bitte geben Sie das Zielverzeichnis ein" }, // console
+        { C_ENTER_JAVA_HOME, "Bitte geben Sie das gewuenschte Java Home Verzeichnis ein (Enter fuer das aktuelle)" }, // console
+        { C_ENGLISH, "Englisch" }, // language
+        { C_EXCLUDE, "Moechten Sie Teile von der Installation ausschliessen ?" }, // installation type
+        { C_GENERATING_START_SCRIPTS, "Start Scripts werden generiert ..." }, // progress
+        { C_GERMAN, "Deutsch" }, // language
+        { C_INCLUDE, "Moechten Sie weitere Teile installieren ?" }, // installation type
+        { C_INEXCLUDE_PARTS, "Folgende Teile stehen zur Auswahl ({0} = keine weiteren)" }, // installation type
+        { C_INSTALL_TYPES, "Die folgenden Installationstypen sind verfuegbar:" }, // installation type
+        { C_INVALID_ANSWER, "Die Antwort {0} ist hier nicht gueltig" }, // error
+        { C_JAVA_VERSION, "Ihre Java Version fuer den Start von Jython ist: {0} / {1}" }, // version
+        { C_MINIMUM, "Minimum (Kern)" }, // installation type
+        { C_NO, "n" }, // answer
+        { C_NO_BIN_DIRECTORY, "Es gibt kein /bin Verzeichnis unterhalb {0}." }, //error
+        { C_NO_JAVA_EXECUTABLE, "Es gibt kein ausfuehrbares java in {0}." }, // error
+        { C_NO_VALID_JAVA, "Keine gueltige Java Version gefunden in {0}." }, // error
+        { C_NON_EMPTY_TARGET_DIRECTORY, "Das Zielverzeichnis {0} enthaelt bereits Daten" }, // error
+        { C_NOT_A_DIRECTORY, "{0} ist kein Verzeichnis. " }, // error
+        { C_NOT_FOUND, "{0} nicht gefunden." }, // error
+        { C_OS_VERSION, "Ihre Betriebssystem Version ist: {0} / {1}" }, // version
+        { C_OVERWRITE_DIRECTORY, "Das Verzeichnis {0} enthaelt bereits Daten, und die Installation wuerde diese ueberschreiben - ok ?" }, // console
+        { C_PACKING_STANDALONE_JAR, "Das standalone " + JarInstaller.JYTHON_JAR + " File wird erstellt ..." }, // progress
+        { C_PROCEED, "Bitte druecken Sie Enter um weiterzufahren" }, // console
+        { C_PROCEED_ANYWAY, "Bitte druecken Sie Enter um trotzdem weiterzufahren" }, // console
+        { C_READ_LICENSE, "Moechten Sie die Lizenzvereinbarung jetzt lesen ?" }, // license
+        { C_READ_README, "Moechten Sie den Inhalt von README jetzt lesen ?" }, // readme
+        { C_SCHEDULED, "{0} zur Installation vorgemerkt" }, // installation type
+        { C_SELECT_INSTALL_TYPE, "Bitte waehlen Sie den Installationstyp" }, // installation type
+        { C_SELECT_LANGUAGE, "Bitte waehlen Sie Ihre Sprache" }, // language
+        { C_SILENT_INSTALLATION, "Die Installation wird ohne Benutzerinteraktion ausgefuehrt" }, // installation mode
+        { C_STANDALONE, "Standalone (ein ausfuehrbares .jar File)" }, //installation mode
+        { C_STANDARD, "Standard (Kern, Bibliotheksmodule, Demos und Beispiele, Dokumentation)" }, // installation type
+        { C_SUCCESS, "Sie haben Jython {0} erfolgreich im Verzeichnis {1} installiert." }, // final
+        { C_SUMMARY, "Zusammenfassung:" }, // summary
+        { C_TO_CURRENT_JAVA, "Warnung: Wechsel zum aktuellen JDK wegen Fehler: {0}." }, // warning
+        { C_UNABLE_CREATE_DIRECTORY, "Fehler beim Erstellen von Verzeichnis {0}." }, // error
+        { C_UNABLE_CREATE_TMPFILE, "Fehler beim Erstellen der temporaeren Datei {0}." }, // error
+        { C_UNABLE_TO_DELETE, "Fehler beim Loeschen von {0}" }, // console
+        { C_UNSCHEDULED, "{0} von der Installation ausgeschlossen" }, // installation type
+        { C_UNSUPPORTED_JAVA, "Diese Java Version ist nicht unterstuetzt." }, // version
+        { C_UNSUPPORTED_OS, "Dieses Betriebssystem ist eventuell nicht vollstaendig unterstuetzt." }, // version
+        { C_USING_TYPE, "Installationstyp ist {0}" }, // installation type
+        { C_VERSION_INFO, "Sie sind im Begriff, Jython Version {0} zu installieren." }, // version
+        { C_WELCOME_TO_JYTHON, "Willkommen bei Jython !" }, // welcome
+        { C_YES, "j" }, // answer
+
+    };
+
+    public Object[][] getContents() {
+        return contents;
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/TextConstants_en.java b/installer/src/java/org/python/util/install/TextConstants_en.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/TextConstants_en.java
@@ -0,0 +1,5 @@
+package org.python.util.install;
+
+public class TextConstants_en extends TextConstants {
+    // need this as placeholder
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/TextKeys.java b/installer/src/java/org/python/util/install/TextKeys.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/TextKeys.java
@@ -0,0 +1,135 @@
+package org.python.util.install;
+
+public interface TextKeys {
+    public static final String ACCEPT = "ACCEPT";
+    public static final String ACCEPT_PROPERTY = "ACCEPT_PROPERTY";
+    public static final String ALL = "ALL";
+    public static final String BROWSE = "BROWSE";
+    public static final String CANCEL = "CANCEL";
+    public static final String CHOOSE_LOCATION = "CHOOSE_LOCATION";
+    public static final String CHOOSE_JRE = "CHOOSE_JRE";
+    public static final String CONFIRM_START = "CONFIRM_START";
+    public static final String CONGRATULATIONS = "CONGRATULATIONS";
+    public static final String CORE = "CORE";
+    public static final String CREATED_DIRECTORY = "CREATED_DIRECTORY";
+    public static final String CURRENT = "CURRENT";
+    public static final String CUSTOM = "CUSTOM";
+    public static final String DEMOS_EXAMPLES = "DEMOS_EXAMPLES";
+    public static final String DIRECTORIES_ONLY = "DIRECTORIES_ONLY";
+    public static final String DOCUMENTATION = "DOCUMENTATION";
+    public static final String DO_NOT_ACCEPT = "DO_NOT_ACCEPT";
+    public static final String EMPTY_TARGET_DIRECTORY = "EMPTY_TARGET_DIRECTORY";
+    public static final String ENGLISH = "ENGLISH";
+    public static final String ERROR = "ERROR";
+    public static final String ERROR_ACCESS_JARFILE = "ERROR_ACCESS_JARFILE";
+    public static final String FINISH = "FINISH";
+    public static final String GENERATING_START_SCRIPTS = "GENERATE_START_SCRIPTS";
+    public static final String GERMAN = "GERMAN";
+    public static final String INFLATING = "INFLATING";
+    public static final String INFORMATION = "INFORMATION";
+    public static final String INSTALLATION_CANCELLED = "INSTALLATION_CANCELLED";
+    public static final String INSTALLATION_IN_PROGRESS = "INSTALLATION_IN_PROGRESS";
+    public static final String INSTALLATION_TYPE_DESCRIPTION = "INSTALLATION_TYPE_DESCRIPTION";
+    public static final String INSTALLATION_TYPE = "INSTALLATION_TYPE";
+    public static final String JAR_NOT_FOUND = "JAR_NOT_FOUND";
+    public static final String JAVA_INFO = "JAVA_INFO";
+    public static final String JYTHON_INSTALL = "JYTHON_INSTALL";
+    public static final String LANGUAGE_PROPERTY = "LANGUAGE_PROPERTY";
+    public static final String LIBRARY_MODULES = "LIBRARY_MODULES";
+    public static final String LICENSE = "LICENSE";
+    public static final String MAYBE_NOT_SUPPORTED = "MAYBE_NOT_SUPPORTED";
+    public static final String MINIMUM = "MINIMUM";
+    public static final String NEXT = "NEXT";
+    public static final String NON_EMPTY_TARGET_DIRECTORY = "NON_EMPTY_TARGET_DIRECTORY";
+    public static final String NO_MANIFEST = "NO_MANIFEST";
+    public static final String NOT_OK = "NOT_OK";
+    public static final String OK = "OK";
+    public static final String OS_INFO = "OS_INFO";
+    public static final String OTHER = "OTHER";
+    public static final String OVERVIEW_DESCRIPTION = "OVERVIEW_DESCRIPTION";
+    public static final String OVERVIEW_TITLE = "OVERVIEW_TITLE";
+    public static final String PACKING_STANDALONE_JAR = "PACKING_STANDALONE_JAR";
+    public static final String PLEASE_ACCEPT_LICENSE = "PLEASE_ACCEPT_LICENSE";
+    public static final String PLEASE_README = "PLEASE_README";
+    public static final String PLEASE_READ_LICENSE = "PLEASE_READ_LICENSE";
+    public static final String PLEASE_WAIT = "PLEASE_WAIT";
+    public static final String PRESS_FINISH = "PRESS_FINISH";
+    public static final String PREVIOUS = "PREVIOUS";
+    public static final String PROGRESS = "PROGRESS";
+    public static final String README = "README";
+    public static final String SELECT = "SELECT";
+    public static final String SELECT_INSTALLATION_TYPE = "SELECT_INSTALLATION_TYPE";
+    public static final String SELECT_JAVA_HOME = "SELECT_JAVA_HOME";
+    public static final String SELECT_LANGUAGE = "SELECT_LANGUAGE";
+    public static final String SELECT_TARGET_DIRECTORY = "SELECT_TARGET_DIRECTORY";
+    public static final String SOURCES = "SOURCES";
+    public static final String STANDARD = "STANDARD";
+    public static final String STANDALONE = "STANDALONE";
+    public static final String SUCCESS = "SUCCESS";
+    public static final String TARGET_DIRECTORY_PROPERTY = "TARGET_DIRECTORY_PROPERTY";
+    public static final String TARGET_JAVA_HOME_PROPERTY = "TARGET_JAVA_HOME_PROPERTY";
+    public static final String UNABLE_CREATE_DIRECTORY = "UNABLE_CREATE_DIRECTORY";
+    public static final String UNABLE_CREATE_FILE = "UNABLE_CREATE_FILE";
+    public static final String UNABLE_TO_DELETE = "UNABLE_TO_DELETE";
+    public static final String UNEXPECTED_URL = "UNEXPECTED_URL";
+    public static final String VERSION_INFO = "VERSION_INFO";
+    public static final String WELCOME_TO_JYTHON = "WELCOME_TO_JYTHON";
+    public static final String ZIP_ENTRY_SIZE = "ZIP_ENTRY_SIZE";
+    public static final String ZIP_ENTRY_TOO_BIG = "ZIP_ENTRY_TOO_BIG";
+
+    // console texts
+    public static final String C_ACCEPT = "C_ACCEPT";
+    public static final String C_ALL = "C_ALL";
+    public static final String C_AT_ANY_TIME_CANCEL = "C_AT_ANY_TIME_CANCEL";
+    public static final String C_AVAILABLE_LANGUAGES = "C_AVAILABLE_LANGUAGES";
+    public static final String C_CHECK_JAVA_VERSION = "C_CHECK_JAVA_VERSION";
+    public static final String C_CLEAR_DIRECTORY = "C_CLEAR_DIRECTORY";
+    public static final String C_CONFIRM_TARGET = "C_CONFIRM_TARGET";
+    public static final String C_CONGRATULATIONS = "C_CONGRATULATIONS";
+    public static final String C_CREATE_DIRECTORY = "C_CREATE_DIRECTORY";
+    public static final String C_ENGLISH = "C_ENGLISH";
+    public static final String C_ENTER_TARGET_DIRECTORY = "C_ENTER_TARGET_DIRECTORY";
+    public static final String C_ENTER_JAVA_HOME = "C_ENTER_JAVA_HOME";
+    public static final String C_EXCLUDE = "C_EXCLUDE";
+    public static final String C_GENERATING_START_SCRIPTS = "C_GENERATE_START_SCRIPTS";
+    public static final String C_GERMAN = "C_GERMAN";
+    public static final String C_INCLUDE = "C_INCLUDE";
+    public static final String C_INEXCLUDE_PARTS = "C_INEXCLUDE_PARTS";
+    public static final String C_INSTALL_TYPES = "C_INSTALL_TYPES";
+    public static final String C_INVALID_ANSWER = "C_INVALID_ANSWER";
+    public static final String C_JAVA_VERSION = "C_JAVA_VERSION";
+    public static final String C_MINIMUM = "C_MINIMUM";
+    public static final String C_NO = "C_NO";
+    public static final String C_NO_BIN_DIRECTORY = "C_NO_BIN_DIRECTORY";
+    public static final String C_NO_JAVA_EXECUTABLE = "C_NO_JAVA_EXECUTABLE";
+    public static final String C_NO_VALID_JAVA = "C_NO_VALID_JAVA";
+    public static final String C_NON_EMPTY_TARGET_DIRECTORY = "C_NON_EMPTY_TARGET_DIRECTORY";
+    public static final String C_NOT_A_DIRECTORY = "C_NOT_A_DIRECTORY";
+    public static final String C_NOT_FOUND = "C_NOT_FOUND";
+    public static final String C_OS_VERSION = "C_OS_VERSION";
+    public static final String C_OVERWRITE_DIRECTORY = "C_OVERWRITE_DIRECTORY";
+    public static final String C_PACKING_STANDALONE_JAR = "C_PACKING_STANDALONE_JAR";
+    public static final String C_PROCEED = "C_PROCEED";
+    public static final String C_PROCEED_ANYWAY = "C_PROCEED_ANYWAY";
+    public static final String C_READ_LICENSE = "C_READ_LICENSE";
+    public static final String C_READ_README = "C_READ_README";
+    public static final String C_SCHEDULED = "C_SCHEDULED";
+    public static final String C_SELECT_INSTALL_TYPE = "C_SELECT_INSTALL_TYPE";
+    public static final String C_SELECT_LANGUAGE = "C_SELECT_LANGUAGE";
+    public static final String C_SILENT_INSTALLATION = "C_SILENT_INSTALLATION";
+    public static final String C_STANDALONE = "C_STANDALONE";
+    public static final String C_STANDARD = "C_STANDARD";
+    public static final String C_SUCCESS = "C_SUCCESS";
+    public static final String C_SUMMARY = "C_SUMMARY";
+    public static final String C_UNABLE_CREATE_TMPFILE = "C_UNABLE_CREATE_TMPFILE";
+    public static final String C_TO_CURRENT_JAVA = "C_TO_CURRENT_JAVA";
+    public static final String C_UNABLE_CREATE_DIRECTORY = "C_UNABLE_CREATE_DIRECTORY";
+    public static final String C_UNABLE_TO_DELETE = "C_UNABLE_TO_DELETE";
+    public static final String C_UNSCHEDULED = "C_UNSCHEDULED";
+    public static final String C_UNSUPPORTED_JAVA = "C_UNSUPPORTED_JAVA";
+    public static final String C_UNSUPPORTED_OS = "C_UNSUPPORTED_OS";
+    public static final String C_USING_TYPE = "C_USING_TYPE";
+    public static final String C_VERSION_INFO = "C_VERSION_INFO";
+    public static final String C_WELCOME_TO_JYTHON = "C_WELCOME_TO_JYTHON";
+    public static final String C_YES = "C_YES";
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/TypePage.java b/installer/src/java/org/python/util/install/TypePage.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/TypePage.java
@@ -0,0 +1,268 @@
+package org.python.util.install;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+
+public class TypePage extends AbstractWizardPage {
+
+    private static final String _CUSTOM_ACTION_COMMAND = "custom";
+
+    private JLabel _label;
+    private JRadioButton _allButton;
+    private JRadioButton _standardButton;
+    private JRadioButton _minimumButton;
+    private JRadioButton _standaloneButton;
+    private JRadioButton _customButton;
+
+    private JCheckBox _core;
+    private JCheckBox _mod;
+    private JCheckBox _demo;
+    private JCheckBox _doc;
+    private JCheckBox _src;
+
+    private boolean _firstTime = true;
+
+    public TypePage() {
+        super();
+        initComponents();
+    }
+
+    private void initComponents() {
+        TypeChangeListener typeChangeListener = new TypeChangeListener();
+
+        _label = new JLabel();
+
+        // radio buttons
+        _allButton = new JRadioButton();
+        _allButton.setActionCommand(Installation.ALL);
+        _allButton.addActionListener(typeChangeListener);
+        _standardButton = new JRadioButton();
+        _standardButton.setActionCommand(Installation.STANDARD);
+        _standardButton.addActionListener(typeChangeListener);
+        _minimumButton = new JRadioButton();
+        _minimumButton.setActionCommand(Installation.MINIMUM);
+        _minimumButton.addActionListener(typeChangeListener);
+        _standaloneButton = new JRadioButton();
+        _standaloneButton.setActionCommand(Installation.STANDALONE);
+        _standaloneButton.addActionListener(typeChangeListener);
+        _customButton = new JRadioButton();
+        _customButton.setActionCommand(_CUSTOM_ACTION_COMMAND);
+        _customButton.addActionListener(typeChangeListener);
+        ButtonGroup radioButtonGroup = new ButtonGroup();
+        radioButtonGroup.add(_allButton);
+        radioButtonGroup.add(_standardButton);
+        radioButtonGroup.add(_minimumButton);
+        radioButtonGroup.add(_standaloneButton);
+        radioButtonGroup.add(_customButton);
+        JPanel radioPanel = new JPanel(new GridLayout(0, 1));
+        radioPanel.add(_allButton);
+        radioPanel.add(_standardButton);
+        radioPanel.add(_minimumButton);
+        radioPanel.add(_standaloneButton);
+        radioPanel.add(_customButton);
+
+        // check boxes
+        _core = new JCheckBox();
+        _core.setEnabled(false);
+        _mod = new JCheckBox();
+        _mod.setEnabled(true);
+        _mod.setActionCommand(InstallerCommandLine.INEXCLUDE_LIBRARY_MODULES);
+        _mod.addActionListener(typeChangeListener);
+        _demo = new JCheckBox();
+        _demo.setEnabled(true);
+        _demo.setActionCommand(InstallerCommandLine.INEXCLUDE_DEMOS_AND_EXAMPLES);
+        _demo.addActionListener(typeChangeListener);
+        _doc = new JCheckBox();
+        _doc.setEnabled(true);
+        _doc.setActionCommand(InstallerCommandLine.INEXCLUDE_DOCUMENTATION);
+        _doc.addActionListener(typeChangeListener);
+        _src = new JCheckBox();
+        _src.setEnabled(true);
+        _src.setActionCommand(InstallerCommandLine.INEXCLUDE_SOURCES);
+        _src.addActionListener(typeChangeListener);
+
+        JPanel checkboxPanel = new JPanel();
+        GridLayout gridLayout = new GridLayout(5, 1);
+        checkboxPanel.setLayout(gridLayout);
+        checkboxPanel.add(_core);
+        checkboxPanel.add(_mod);
+        checkboxPanel.add(_demo);
+        checkboxPanel.add(_doc);
+        checkboxPanel.add(_src);
+
+        JPanel panel = new JPanel();
+        GridBagLayout gridBagLayout = new GridBagLayout();
+        panel.setLayout(gridBagLayout);
+        GridBagConstraints gridBagConstraints = newGridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.gridwidth = 2;
+        panel.add(_label, gridBagConstraints);
+        gridBagConstraints.gridwidth = 1;
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        panel.add(radioPanel, gridBagConstraints);
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 1;
+        panel.add(checkboxPanel, gridBagConstraints);
+
+        add(panel);
+    }
+
+    protected String getTitle() {
+        return getText(INSTALLATION_TYPE);
+    }
+
+    protected String getDescription() {
+        return getText(INSTALLATION_TYPE_DESCRIPTION);
+    }
+
+    protected boolean isCancelVisible() {
+        return true;
+    }
+
+    protected boolean isPreviousVisible() {
+        return true;
+    }
+
+    protected boolean isNextVisible() {
+        return true;
+    }
+
+    protected JComponent getFocusField() {
+        InstallationType installationType = getInstallationType();
+        if (installationType.isAll()) {
+            return _allButton;
+        } else if (installationType.isMinimum()) {
+            return _minimumButton;
+        } else if (installationType.isStandalone()) {
+            return _standaloneButton;
+        } else if (installationType.isStandard()) {
+            return _standardButton;
+        } else {
+            return _customButton;
+        }
+    }
+
+    protected void activate() {
+        _label.setText(getText(SELECT_INSTALLATION_TYPE) + ": ");
+        _allButton.setText(getText(ALL));
+        _standardButton.setText(getText(STANDARD));
+        _minimumButton.setText(getText(MINIMUM));
+        _standaloneButton.setText(getText(STANDALONE));
+        _customButton.setText(getText(CUSTOM));
+        InstallationType installationType = getInstallationType();
+        if (installationType.isAll()) {
+            _allButton.setSelected(true);
+        } else if (installationType.isMinimum()) {
+            _minimumButton.setSelected(true);
+        } else if (installationType.isStandalone()) {
+            _standaloneButton.setSelected(true);
+        } else if (installationType.isStandard()) {
+            _standardButton.setSelected(true);
+        } else {
+            _customButton.setSelected(true);
+        }
+        _core.setText(getText(CORE));
+        _mod.setText(getText(LIBRARY_MODULES));
+        _demo.setText(getText(DEMOS_EXAMPLES));
+        _doc.setText(getText(DOCUMENTATION));
+        _src.setText(getText(SOURCES));
+        setCheckboxes(installationType);
+    }
+
+    protected void passivate() {
+    }
+
+    protected void beforeValidate() {
+    }
+
+    private InstallationType getInstallationType() {
+        InstallationType installationType;
+        if (_firstTime) {
+            _firstTime = false;
+            installationType = new InstallationType();
+            installationType.setStandard();
+            FrameInstaller.setInstallationType(installationType);
+        }
+        installationType = FrameInstaller.getInstallationType();
+        return installationType;
+    }
+
+    private final class TypeChangeListener implements ActionListener {
+        public void actionPerformed(ActionEvent e) {
+            InstallationType installationType = FrameInstaller.getInstallationType();
+            String actionCommand = e.getActionCommand();
+            if (Installation.ALL.equals(actionCommand)) {
+                installationType.setAll();
+                setCheckboxes(installationType);
+            } else if (Installation.STANDARD.equals(actionCommand)) {
+                installationType.setStandard();
+                setCheckboxes(installationType);
+            } else if (Installation.MINIMUM.equals(actionCommand)) {
+                installationType.setMinimum();
+                setCheckboxes(installationType);
+            } else if (Installation.STANDALONE.equals(actionCommand)) {
+                installationType.setStandalone();
+                setCheckboxes(installationType);
+            } else if (_CUSTOM_ACTION_COMMAND.equals(actionCommand)) {
+                _mod.setEnabled(true);
+                _demo.setEnabled(true);
+                _doc.setEnabled(true);
+                _src.setEnabled(true);
+            } else {
+                boolean selected = ((JCheckBox) e.getSource()).isSelected();
+                if (InstallerCommandLine.INEXCLUDE_LIBRARY_MODULES.equals(actionCommand)) {
+                    if (selected) {
+                        installationType.addLibraryModules();
+                    } else {
+                        installationType.removeLibraryModules();
+                    }
+                } else if (InstallerCommandLine.INEXCLUDE_DEMOS_AND_EXAMPLES.equals(actionCommand)) {
+                    if (selected) {
+                        installationType.addDemosAndExamples();
+                    } else {
+                        installationType.removeDemosAndExamples();
+                    }
+                } else if (InstallerCommandLine.INEXCLUDE_DOCUMENTATION.equals(actionCommand)) {
+                    if (selected) {
+                        installationType.addDocumentation();
+                    } else {
+                        installationType.removeDocumentation();
+                    }
+                } else if (InstallerCommandLine.INEXCLUDE_SOURCES.equals(actionCommand)) {
+                    if (selected) {
+                        installationType.addSources();
+                    } else {
+                        installationType.removeSources();
+                    }
+                }
+            }
+            FrameInstaller.setInstallationType(installationType);
+        }
+    }
+
+    void setCheckboxes(InstallationType installationType) {
+        _core.setSelected(true);
+        _mod.setSelected(installationType.installLibraryModules());
+        _demo.setSelected(installationType.installDemosAndExamples());
+        _doc.setSelected(installationType.installDocumentation());
+        _src.setSelected(installationType.installSources());
+        _standaloneButton.setSelected(installationType.isStandalone());
+        _mod.setEnabled(!installationType.isPredefined());
+        _demo.setEnabled(!installationType.isPredefined());
+        _doc.setEnabled(!installationType.isPredefined());
+        _src.setEnabled(!installationType.isPredefined());
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/UnicodeSequences.java b/installer/src/java/org/python/util/install/UnicodeSequences.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/UnicodeSequences.java
@@ -0,0 +1,19 @@
+package org.python.util.install;
+
+/**
+ * Some unicode special characters
+ * 
+ * @see http://www.unicode.org/charts/PDF/U0080.pdf
+ */
+public interface UnicodeSequences {
+
+    public static final String A2 = "\u00C4"; // German A umlaut: A with two dots above
+    public static final String a2 = "\u00E4"; // German a umlaut: a with two dots above
+
+    public static final String O2 = "\u00D6"; // German O umlaut: O with two dots above
+    public static final String o2 = "\u00F6"; // German o umlaut: o with two dots above
+
+    public static final String U2 = "\u00DC"; // German U umlaut: U with two dots above
+    public static final String u2 = "\u00FC"; // German u umlaut: u with two dots above
+
+}
diff --git a/installer/src/java/org/python/util/install/ValidationEvent.java b/installer/src/java/org/python/util/install/ValidationEvent.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ValidationEvent.java
@@ -0,0 +1,14 @@
+package org.python.util.install;
+
+import java.util.EventObject;
+
+public class ValidationEvent extends EventObject {
+
+    public ValidationEvent(AbstractWizardPage source) {
+        super(source);
+    }
+
+    public AbstractWizardPage getWizardPage() {
+        return (AbstractWizardPage) source;
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ValidationException.java b/installer/src/java/org/python/util/install/ValidationException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ValidationException.java
@@ -0,0 +1,19 @@
+package org.python.util.install;
+
+public class ValidationException extends Exception {
+    public ValidationException() {
+        super();
+    }
+
+    public ValidationException(String message) {
+        super(message);
+    }
+
+    public ValidationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ValidationException(Throwable cause) {
+        super(cause);
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ValidationInformationException.java b/installer/src/java/org/python/util/install/ValidationInformationException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ValidationInformationException.java
@@ -0,0 +1,21 @@
+package org.python.util.install;
+
+public class ValidationInformationException extends Exception {
+
+    public ValidationInformationException() {
+        super();
+    }
+
+    public ValidationInformationException(String message) {
+        super(message);
+    }
+
+    public ValidationInformationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ValidationInformationException(Throwable cause) {
+        super(cause);
+    }
+
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/ValidationListener.java b/installer/src/java/org/python/util/install/ValidationListener.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/ValidationListener.java
@@ -0,0 +1,11 @@
+package org.python.util.install;
+
+public interface ValidationListener {
+    public void validationFailed(ValidationEvent event, ValidationException exception);
+
+    public void validationInformationRequired(ValidationEvent event, ValidationInformationException exception);
+
+    public void validationStarted(ValidationEvent event);
+
+    public void validationSucceeded(ValidationEvent event);
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/Wizard.java b/installer/src/java/org/python/util/install/Wizard.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/Wizard.java
@@ -0,0 +1,90 @@
+package org.python.util.install;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import javax.swing.JOptionPane;
+
+import org.python.util.install.driver.Autotest;
+
+public class Wizard extends AbstractWizard implements TextKeys {
+
+    public Wizard(JarInfo jarInfo, Autotest autotest) {
+        super();
+
+        setTitle(getText(JYTHON_INSTALL));
+
+        LanguagePage languagePage = new LanguagePage(jarInfo);
+        LicensePage licensePage = new LicensePage(jarInfo);
+        licensePage.setValidator(new LicensePageValidator(licensePage));
+        TypePage typePage = new TypePage();
+        DirectorySelectionPage directoryPage = new DirectorySelectionPage(jarInfo);
+        directoryPage.setValidator(new DirectorySelectionPageValidator(directoryPage));
+        JavaSelectionPage javaPage = new JavaSelectionPage();
+        javaPage.setValidator(new JavaSelectionPageValidator(javaPage));
+        OverviewPage overviewPage = new OverviewPage();
+        ProgressPage progressPage = new ProgressPage(jarInfo, autotest);
+        ReadmePage readmePage = new ReadmePage(jarInfo);
+        SuccessPage successPage = new SuccessPage(jarInfo);
+
+        this.addPage(languagePage);
+        this.addPage(licensePage);
+        this.addPage(typePage);
+        this.addPage(directoryPage);
+        this.addPage(javaPage);
+        this.addPage(overviewPage);
+        this.addPage(progressPage);
+        this.addPage(readmePage);
+        this.addPage(successPage);
+
+        setSize(720, 330);
+        centerOnScreen();
+        validate();
+    }
+
+    protected boolean finish() {
+        return true;
+    }
+
+    protected String getCancelString() {
+        return getText(CANCEL);
+    }
+
+    protected String getFinishString() {
+        return getText(FINISH);
+    }
+
+    protected String getNextString() {
+        return getText(NEXT);
+    }
+
+    protected String getPreviousString() {
+        return getText(PREVIOUS);
+    }
+
+    public void validationStarted(ValidationEvent event) {
+    }
+
+    public void validationFailed(ValidationEvent event, ValidationException exception) {
+        JOptionPane.showMessageDialog(this, exception.getMessage(), getText(TextKeys.ERROR), JOptionPane.ERROR_MESSAGE);
+    }
+
+    public void validationInformationRequired(ValidationEvent event, ValidationInformationException exception) {
+        JOptionPane.showMessageDialog(this, exception.getMessage(), getText(INFORMATION),
+                JOptionPane.INFORMATION_MESSAGE);
+    }
+
+    public void validationSucceeded(ValidationEvent event) {
+    }
+
+    private final String getText(String textKey) {
+        return Installation.getText(textKey);
+    }
+
+    private void centerOnScreen() {
+        Dimension dim = getToolkit().getScreenSize();
+        Rectangle rectBounds = getBounds();
+        setLocation((dim.width - rectBounds.width) / 2, (dim.height - rectBounds.height) / 2);
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/WizardEvent.java b/installer/src/java/org/python/util/install/WizardEvent.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/WizardEvent.java
@@ -0,0 +1,13 @@
+package org.python.util.install;
+
+import java.util.EventObject;
+
+public class WizardEvent extends EventObject {
+    public WizardEvent(AbstractWizard source) {
+        super(source);
+    }
+
+    public AbstractWizard getWizard() {
+        return (AbstractWizard) source;
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/WizardHeader.java b/installer/src/java/org/python/util/install/WizardHeader.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/WizardHeader.java
@@ -0,0 +1,89 @@
+package org.python.util.install;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JSeparator;
+
+public class WizardHeader extends AbstractWizardHeader {
+    private static final Dimension _iconSize = new Dimension(100, 60);
+
+    private JLabel _descriptionLabel;
+    private JSeparator _headerSeparator;
+    private JLabel _iconLabel;
+    private JLabel _titleLabel;
+
+    WizardHeader() {
+        super();
+        initComponents();
+    }
+
+    private void initComponents() {
+        GridBagConstraints gridBagConstraints;
+
+        _titleLabel = new JLabel();
+        _descriptionLabel = new JLabel();
+        _iconLabel = new JLabel();
+        _headerSeparator = new JSeparator();
+
+        setLayout(new GridBagLayout());
+
+        setBackground(new Color(255, 255, 255));
+        _titleLabel.setFont(_titleLabel.getFont().deriveFont(Font.BOLD, 14f));
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.insets = new Insets(2, 2, 2, 2);
+        gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        add(_titleLabel, gridBagConstraints);
+
+        _descriptionLabel.setFont(_descriptionLabel.getFont().deriveFont(Font.PLAIN));
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.insets = new Insets(2, 7, 2, 2);
+        gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.weighty = 1.0;
+        add(_descriptionLabel, gridBagConstraints);
+
+        _iconLabel.setMinimumSize(_iconSize);
+        _iconLabel.setMaximumSize(_iconSize);
+        _iconLabel.setPreferredSize(_iconSize);
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.gridheight = 2;
+        gridBagConstraints.insets = new Insets(2, 2, 2, 2);
+        gridBagConstraints.anchor = GridBagConstraints.NORTHEAST;
+        add(_iconLabel, gridBagConstraints);
+
+        gridBagConstraints = new GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        gridBagConstraints.gridwidth = 2;
+        gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.weightx = 1.0;
+        add(_headerSeparator, gridBagConstraints);
+    }
+
+    protected void setDescription(String description) {
+        _descriptionLabel.setText(description);
+    }
+
+    protected void setIcon(ImageIcon icon) {
+        _iconLabel.setIcon(icon);
+    }
+
+    protected void setTitle(String title) {
+        _titleLabel.setText(title);
+    }
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/WizardListener.java b/installer/src/java/org/python/util/install/WizardListener.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/WizardListener.java
@@ -0,0 +1,13 @@
+package org.python.util.install;
+
+public interface WizardListener {
+    public void wizardCancelled(WizardEvent event);
+
+    public void wizardFinished(WizardEvent event);
+
+    public void wizardNext(WizardEvent event);
+
+    public void wizardPrevious(WizardEvent event);
+
+    public void wizardStarted(WizardEvent event);
+}
\ No newline at end of file
diff --git a/installer/src/java/org/python/util/install/driver/Autotest.java b/installer/src/java/org/python/util/install/driver/Autotest.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/Autotest.java
@@ -0,0 +1,274 @@
+package org.python.util.install.driver;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.python.util.install.FileHelper;
+import org.python.util.install.Installation;
+import org.python.util.install.InstallationListener;
+import org.python.util.install.InstallerCommandLine;
+import org.python.util.install.JavaHomeHandler;
+import org.python.util.install.JavaVersionTester;
+import org.python.util.install.Installation.JavaVersionInfo;
+
+public abstract class Autotest implements InstallationListener {
+
+    private static final String _DIR_SUFFIX = "_dir";
+
+    private static int _count = 0; // unique test number
+    private static File _rootDirectory = null;
+    private static JavaVersionInfo _systemDefaultJavaVersion;
+
+    private String _name;
+    private File _targetDir;
+    private JavaHomeHandler _javaHomeHandler;
+    private boolean _verbose;
+    private String[] _commandLineArgs;
+    private Verifier _verifier;
+
+    /**
+     * constructor
+     * 
+     * @throws IOException
+     * @throws DriverException
+     */
+    protected Autotest(InstallerCommandLine commandLine) throws IOException, DriverException {
+        _count++;
+        buildName();
+        if (_rootDirectory == null) {
+            createRootDirectory();
+        }
+        createTargetDirectory();
+        setCommandLineArgs(new String[0]); // a priori value
+        _verbose = commandLine.hasVerboseOption();
+        _javaHomeHandler = commandLine.getJavaHomeHandler();
+    }
+
+    /**
+     * @return the root directory for all test installations
+     */
+    protected static File getRootDir() {
+        return _rootDirectory;
+    }
+
+    /**
+     * @return the target directory of this test
+     */
+    protected File getTargetDir() {
+        return _targetDir;
+    }
+
+    /**
+     * @return the name of this test
+     */
+    protected String getName() {
+        return _name;
+    }
+
+    /**
+     * @return the array of command line arguments
+     */
+    protected String[] getCommandLineArgs() {
+        return _commandLineArgs;
+    }
+
+    /**
+     * set the array of command line arguments
+     * 
+     * @param commandLineArgs
+     */
+    protected void setCommandLineArgs(String[] commandLineArgs) {
+        _commandLineArgs = commandLineArgs;
+    }
+
+    /**
+     * @return the java home handler, can be asked for deviation using <code>isDeviation()</code>.
+     */
+    protected JavaHomeHandler getJavaHomeHandler() {
+        return _javaHomeHandler;
+    }
+
+    /**
+     * @return <code>true</code> if this test should be verbose
+     */
+    protected boolean isVerbose() {
+        return _verbose;
+    }
+
+    /**
+     * @return the name suffix for this test
+     */
+    protected abstract String getNameSuffix();
+
+    /**
+     * @throws DriverException if the target directory does not exist or is empty (installation failed)
+     */
+    protected void assertTargetDirNotEmpty() throws DriverException {
+        File targetDir = getTargetDir();
+        if (targetDir != null) {
+            if (targetDir.exists() && targetDir.isDirectory()) {
+                if (targetDir.listFiles().length > 0) {
+                    return;
+                }
+            }
+        }
+        throw new DriverException("installation failed for " + targetDir.getAbsolutePath());
+    }
+
+    /**
+     * Convenience method to add the additional arguments, if specified behind the -A option
+     * <p>
+     * This adds (if present):
+     * <ul>
+     * <li> target directory (should always be present)
+     * <li> verbose
+     * <li> jre
+     * </ul>
+     */
+    protected void addAdditionalArguments() {
+        if (getTargetDir() != null) {
+            addArgument("-d");
+            addArgument(getTargetDir().getAbsolutePath());
+        }
+        if (isVerbose()) {
+            addArgument("-v");
+        }
+        JavaHomeHandler javaHomeHandler = getJavaHomeHandler();
+        if (javaHomeHandler.isDeviation() && javaHomeHandler.isValidHome()) {
+            addArgument("-j");
+            addArgument(javaHomeHandler.getHome().getAbsolutePath());
+        }
+    }
+
+    /**
+     * Add an additional String argument to the command line arguments
+     * 
+     * @param newArgument
+     */
+    protected void addArgument(String newArgument) {
+        setCommandLineArgs(addArgument(newArgument, getCommandLineArgs()));
+    }
+
+    /**
+     * set the verifier
+     */
+    protected void setVerifier(Verifier verifier) {
+        _verifier = verifier;
+        _verifier.setTargetDir(getTargetDir());
+    }
+
+    protected Verifier getVerifier() {
+        return _verifier;
+    }
+
+    //
+    // private stuff
+    //
+
+    /**
+     * build a test name containing some special characters (which will be used to create the target
+     * directory), and store it into <code>_name</code>.
+     */
+    private void buildName() {
+        StringBuilder b = new StringBuilder(24);
+        if (_count <= 99) {
+            b.append('0');
+        }
+        if (_count <= 9) {
+            b.append('0');
+        }
+        b.append(_count);
+        // explicitly use a blank, to nail down some platform specific problems
+        b.append(' ');
+        // add an exclamation mark if possible (see issue #1208)
+        if(canHandleExclamationMarks()) {
+            b.append('!');
+        }
+        b.append(getNameSuffix());
+        b.append('_');
+        _name = b.toString();
+    }
+
+    /**
+     * Add the new argument to the args array
+     * 
+     * @param newArgument
+     * @param args
+     * 
+     * @return the new String array, with size increased by 1
+     */
+    private String[] addArgument(String newArgument, String[] args) {
+        String[] newArgs = new String[args.length + 1];
+        for (int i = 0; i < args.length; i++) {
+            newArgs[i] = args[i];
+        }
+        newArgs[args.length] = newArgument;
+        return newArgs;
+    }
+
+    /**
+     * create the root directory for all automatic installations
+     * <p>
+     * assumed to be a subdirectory of java.io.tmpdir
+     * 
+     * @throws IOException
+     * @throws DriverException
+     */
+    private void createRootDirectory() throws IOException, DriverException {
+        File tmpFile = File.createTempFile("jython.autoinstall.root_", _DIR_SUFFIX);
+        if (FileHelper.createTempDirectory(tmpFile)) {
+            _rootDirectory = tmpFile;
+        } else {
+            throw new DriverException("unable to create root temporary directory");
+        }
+    }
+
+    /**
+     * create a target directory for a test installation
+     * 
+     * @throws IOException
+     * @throws DriverException
+     */
+    private void createTargetDirectory() throws IOException, DriverException {
+        File tmpFile = File.createTempFile(getName(), _DIR_SUFFIX, _rootDirectory);
+        if (FileHelper.createTempDirectory(tmpFile)) {
+            _targetDir = tmpFile;
+        } else {
+            throw new DriverException("unable to create temporary target directory");
+        }
+    }
+
+    /**
+     * Determine if the target directory may contain an exclamation mark (see also issue #1208).
+     * <p>
+     * Autotests can handle exclamation marks, if both the running and the system default java
+     * specification versions are 1.6 or higher. Class.getResource() was fixed for JDK 1.6, but only if the directory name does not end with '!'...
+     * <p>
+     * Currently there is no way on windows, because the enabledelayedexpansion in jython.bat cannot
+     * handle exclamation marks in variable names.
+     * 
+     * @return <code>true</code> if we can handle exclamation marks, <code>false</code> otherwise
+     */
+    private boolean canHandleExclamationMarks() {
+        boolean exclamation = false;
+        if (!Installation.isWindows()) {
+            // get the running java specification version
+            String specificationVersion = System.getProperty(JavaVersionTester.JAVA_SPECIFICATION_VERSION,
+                                                             "");
+            if (Installation.getJavaSpecificationVersion(specificationVersion) > 15) {
+                // get the system default java version
+                if (_systemDefaultJavaVersion == null) {
+                    _systemDefaultJavaVersion = Installation.getDefaultJavaVersion();
+                }
+                if (_systemDefaultJavaVersion.getErrorCode() == Installation.NORMAL_RETURN) {
+                    specificationVersion = _systemDefaultJavaVersion.getSpecificationVersion();
+                    if (Installation.getJavaSpecificationVersion(specificationVersion) > 15) {
+                        exclamation = true;
+                    }
+                }
+            }
+        }
+        return exclamation;
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/driver/ConsoleAutotest.java b/installer/src/java/org/python/util/install/driver/ConsoleAutotest.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/ConsoleAutotest.java
@@ -0,0 +1,30 @@
+package org.python.util.install.driver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.python.util.install.InstallerCommandLine;
+
+public class ConsoleAutotest extends SilentAutotest {
+
+    private Collection _answers;
+
+    protected ConsoleAutotest(InstallerCommandLine commandLine) throws IOException, DriverException {
+        super(commandLine);
+        _answers = new ArrayList(50);
+    }
+
+    protected void addAnswer(String answer) {
+        _answers.add(answer);
+    }
+
+    protected Collection getAnswers() {
+        return _answers;
+    }
+
+    protected String getNameSuffix() {
+        return "consoleTest";
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/driver/ConsoleDriver.java b/installer/src/java/org/python/util/install/driver/ConsoleDriver.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/ConsoleDriver.java
@@ -0,0 +1,58 @@
+package org.python.util.install.driver;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * A class driving another class, while the other class is performing console I/O.
+ * 
+ * <pre>
+ *   (2)  [Driver]   receives question  [Tunnel]   sends question   [Console]  (1)
+ *   (3)  [Driver]   sends answer       [Tunnel]   receives answer  [Console]  (4)
+ * </pre>
+ */
+public class ConsoleDriver extends Thread {
+
+    private Tunnel _tunnel;
+    private Collection _answers;
+
+    public ConsoleDriver(Tunnel tunnel, Collection answers) {
+        _tunnel = tunnel;
+        _answers = answers;
+    }
+
+    /**
+     * Send answers in the correct sequence, as soon as the question is asked.
+     */
+    public void run() {
+        Iterator answersIterator = _answers.iterator();
+        while (answersIterator.hasNext()) {
+            String answer = (String) answersIterator.next();
+            try {
+                readLine();
+                sendAnswer(answer);
+            } catch (IOException e) {
+                e.printStackTrace();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void sendAnswer(String answer) throws IOException, InterruptedException {
+        Thread.sleep(100); // wait to be sure the question is really issued on the other end of the tunnel
+        System.out.println(" -> driving: '" + answer + "'");
+        answer += Tunnel.NEW_LINE;
+        _tunnel.getAnswerSenderStream().write(answer.getBytes());
+        _tunnel.getAnswerSenderStream().flush();
+    }
+
+    private void readLine() throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(_tunnel.getQuestionReceiverStream()));
+        reader.readLine();
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/driver/DriverException.java b/installer/src/java/org/python/util/install/driver/DriverException.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/DriverException.java
@@ -0,0 +1,17 @@
+package org.python.util.install.driver;
+
+public class DriverException extends Exception {
+
+    public DriverException() {
+        super();
+    }
+
+    public DriverException(String message) {
+        super(message);
+    }
+
+    public DriverException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/driver/GuiAutotest.java b/installer/src/java/org/python/util/install/driver/GuiAutotest.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/GuiAutotest.java
@@ -0,0 +1,188 @@
+package org.python.util.install.driver;
+
+import java.awt.AWTException;
+import java.awt.Robot;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.python.util.install.InstallerCommandLine;
+
+public class GuiAutotest extends Autotest {
+
+    private static final int _DEFAULT_DELAY = 500; // ms
+
+    private Robot _robot;
+    private List _keyActions;
+    private boolean _waiting = false;
+
+    protected GuiAutotest(InstallerCommandLine commandLine) throws IOException, DriverException {
+        super(commandLine);
+        _keyActions = new ArrayList();
+        // pass in the target directory, verbositiy
+        String[] args = new String[] { "-d", getTargetDir().getAbsolutePath() };
+        setCommandLineArgs(args);
+        addAdditionalArguments();
+    }
+
+    protected String getNameSuffix() {
+        return "guiTest";
+    }
+
+    /**
+     * add a normal key action (press and release)
+     * 
+     * @param keyCode
+     */
+    protected void addKeyAction(int keyCode) {
+        KeyAction keyAction = new KeyAction(keyCode);
+        addKeyAction(keyAction);
+    }
+
+    /**
+     * add a normal key action (press and release), with a specific delay
+     * 
+     * @param keyCode
+     * @param delay
+     */
+    protected void addKeyAction(int keyCode, int delay) {
+        KeyAction keyAction = new KeyAction(keyCode, delay);
+        addKeyAction(keyAction);
+    }
+
+    /**
+     * add a key action (press and release) which waits before executing
+     * 
+     * @param keyCode
+     */
+    protected void addWaitingKeyAction(int keyCode) {
+        KeyAction keyAction = new KeyAction(keyCode, true);
+        addKeyAction(keyAction);
+    }
+
+    protected void setWaiting(boolean waiting) {
+        _waiting = waiting;
+    }
+
+    /**
+     * execute a single gui auto test
+     * 
+     * @throws DriverException
+     */
+    protected void execute() throws DriverException {
+        try {
+            _robot = new Robot();
+
+            System.out.println("waiting 2 seconds for the first gui ... please do not change focus");
+            _robot.delay(2000); // initial gui load
+
+            Iterator actionsIterator = _keyActions.iterator();
+            while (actionsIterator.hasNext()) {
+                KeyAction keyAction = (KeyAction) actionsIterator.next();
+                setWaiting(keyAction.isWait());
+                if (isWaiting()) {
+                    System.out.println("waiting for the installation to finish ...");
+                }
+                while (isWaiting()) {
+                    try {
+                        Thread.sleep(_DEFAULT_DELAY);
+                    } catch (InterruptedException e) {
+                        throw new DriverException(e);
+                    }
+                }
+                executeKeyAction(keyAction);
+            }
+        } catch (AWTException ae) {
+            throw new DriverException(ae);
+        }
+
+    }
+
+    /**
+     * General KeyAction
+     */
+    protected static class KeyAction {
+        private int _keyCode;
+        private int _delay;
+        private boolean _wait;
+
+        /**
+         * @param keyCode
+         */
+        protected KeyAction(int keyCode) {
+            this(keyCode, _DEFAULT_DELAY);
+        }
+
+        /**
+         * @param keyCode
+         * @param delay in ms
+         */
+        protected KeyAction(int keyCode, int delay) {
+            super();
+            setKeyCode(keyCode);
+            setDelay(delay);
+        }
+
+        /**
+         * @param keyCode
+         * @param wait true if we should wait before executing this key action
+         */
+        protected KeyAction(int keyCode, boolean wait) {
+            this(keyCode, _DEFAULT_DELAY);
+            setWait(wait);
+        }
+
+        protected void setKeyCode(int keyCode) {
+            _keyCode = keyCode;
+        }
+
+        protected void setDelay(int delay) {
+            _delay = delay;
+        }
+
+        protected int getDelay() {
+            return _delay;
+        }
+
+        protected int getKeyCode() {
+            return _keyCode;
+        }
+
+        protected void setWait(boolean wait) {
+            _wait = wait;
+        }
+
+        protected boolean isWait() {
+            return _wait;
+        }
+    }
+
+    //
+    // interface InstallationListener
+    //
+
+    public void progressFinished() {
+        setWaiting(false);
+    }
+
+    //
+    // private stuff
+    //
+
+    private boolean isWaiting() {
+        return _waiting;
+    }
+
+    private void addKeyAction(KeyAction keyAction) {
+        _keyActions.add(keyAction);
+    }
+
+    private void executeKeyAction(KeyAction keyAction) {
+        _robot.delay(keyAction.getDelay());
+        _robot.keyPress(keyAction.getKeyCode());
+        _robot.delay(20); // delay was handled before press
+        _robot.keyRelease(keyAction.getKeyCode());
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/driver/InstallationDriver.java b/installer/src/java/org/python/util/install/driver/InstallationDriver.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/InstallationDriver.java
@@ -0,0 +1,416 @@
+package org.python.util.install.driver;
+
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.python.util.install.ConsoleInstaller;
+import org.python.util.install.Installation;
+import org.python.util.install.InstallerCommandLine;
+import org.python.util.install.JavaHomeHandler;
+
+public class InstallationDriver {
+    private SilentAutotest[] _silentTests;
+    private ConsoleAutotest[] _consoleTests;
+    private GuiAutotest[] _guiTests;
+    private InstallerCommandLine _commandLine;
+
+    /**
+     * construct the driver
+     * 
+     * @param commandLine the console arguments
+     * 
+     * @throws IOException
+     * @throws DriverException
+     */
+    public InstallationDriver(InstallerCommandLine commandLine) throws DriverException {
+        _commandLine = commandLine;
+        try {
+            buildSilentTests();
+            buildConsoleTests();
+            buildGuiTests();
+        } catch (IOException ioe) {
+            throw new DriverException(ioe);
+        }
+    }
+
+    /**
+     * execute all the automatic tests
+     * 
+     * @throws DriverException
+     */
+    public void drive() throws DriverException {
+        try {
+            // silent tests
+            for (int i = 0; i < _silentTests.length; i++) {
+                driveSilentTest(_silentTests[i]);
+            }
+            // console tests
+            for (int i = 0; i < _consoleTests.length; i++) {
+                driveConsoleTest(_consoleTests[i]);
+            }
+            // gui tests
+            for (int i = 0; i < _guiTests.length; i++) {
+                driveGuiTest(_guiTests[i]);
+            }
+        } catch (IOException ioe) {
+            throw new DriverException(ioe);
+        }
+    }
+
+    /**
+     * execute a single console test
+     * 
+     * @param consoleTest
+     * @throws DriverException
+     * @throws IOException
+     * @throws IOException
+     */
+    private void driveConsoleTest(ConsoleAutotest consoleTest) throws DriverException, IOException {
+        Tunnel _tunnel;
+        _tunnel = new Tunnel();
+        // have to fork off the driver thread first
+        ConsoleDriver driver = new ConsoleDriver(_tunnel, consoleTest.getAnswers());
+        driver.start();
+        // now do the installation
+        Installation.driverMain(consoleTest.getCommandLineArgs(), consoleTest, _tunnel);
+        _tunnel.close();
+        validate(consoleTest);
+    }
+
+    /**
+     * execute a single silent test
+     * 
+     * @param silentTest
+     * @throws DriverException
+     */
+    private void driveSilentTest(SilentAutotest silentTest) throws DriverException {
+        Installation.driverMain(silentTest.getCommandLineArgs(), silentTest, null); // only a thin wrapper
+        validate(silentTest);
+    }
+
+    /**
+     * execute a single gui test
+     * 
+     * @param guiTest
+     * @throws DriverException
+     */
+    private void driveGuiTest(GuiAutotest guiTest) throws DriverException {
+        Installation.driverMain(guiTest.getCommandLineArgs(), guiTest, null); // only a thin wrapper
+        guiTest.execute();
+        validate(guiTest);
+    }
+
+    /**
+     * perform validations after the test was run
+     * 
+     * @param autoTest
+     * @throws DriverException
+     */
+    private void validate(Autotest autoTest) throws DriverException {
+        autoTest.assertTargetDirNotEmpty();
+        if (autoTest.getVerifier() != null) {
+            System.out.println("verifying installation - this can take a while ...");
+            autoTest.getVerifier().verify();
+            System.out.println("... installation ok.\n");
+        }
+    }
+
+    /**
+     * @return the command line the autotest session was started with
+     */
+    private InstallerCommandLine getOriginalCommandLine() {
+        return _commandLine;
+    }
+
+    /**
+     * build all the silent tests
+     * 
+     * @throws IOException
+     * @throws DriverException
+     */
+    private void buildSilentTests() throws IOException, DriverException {
+        List<SilentAutotest> silentTests = new ArrayList<SilentAutotest>(50);
+
+        SilentAutotest test1 = new SilentAutotest(getOriginalCommandLine());
+        String[] arguments = new String[] { "-s" };
+        test1.setCommandLineArgs(arguments);
+        test1.addAdditionalArguments(); // this also adds target directory
+        test1.setVerifier(new NormalVerifier());
+        silentTests.add(test1);
+
+        SilentAutotest test2 = new SilentAutotest(getOriginalCommandLine());
+        arguments = new String[] { "-s", "-t", "minimum" };
+        test2.setCommandLineArgs(arguments);
+        test2.addAdditionalArguments();
+        test2.setVerifier(new NormalVerifier());
+        silentTests.add(test2);
+
+        SilentAutotest test3 = new SilentAutotest(getOriginalCommandLine());
+        arguments = new String[] { "-s", "-t", "standalone" };
+        test3.setCommandLineArgs(arguments);
+        test3.addAdditionalArguments();
+        test3.setVerifier(new StandaloneVerifier());
+        silentTests.add(test3);
+
+        // build array
+        int size = silentTests.size();
+        _silentTests = new SilentAutotest[size];
+        Iterator<SilentAutotest> silentIterator = silentTests.iterator();
+        for (int i = 0; i < size; i++) {
+            _silentTests[i] = silentIterator.next();
+        }
+    }
+
+    /**
+     * build all the console tests
+     * 
+     * @throws IOException
+     * @throws DriverException
+     */
+    private void buildConsoleTests() throws IOException, DriverException {
+        List<ConsoleAutotest> consoleTests = new ArrayList<ConsoleAutotest>(5);
+        final String[] arguments;
+        if (getOriginalCommandLine().hasVerboseOption()) {
+            arguments = new String[] { "-c", "-v" };
+        } else {
+            arguments = new String[] { "-c" };
+        }
+        // do NOT call addAdditionalArguments()
+
+        ConsoleAutotest test1 = new ConsoleAutotest(getOriginalCommandLine());
+        test1.setCommandLineArgs(arguments);
+        test1.addAnswer("e"); // language
+        test1.addAnswer("n"); // no read of license
+        test1.addAnswer("y"); // accept license
+        test1.addAnswer("3"); // type: minimum
+        test1.addAnswer("n"); // include: nothing
+        test1.addAnswer(test1.getTargetDir().getAbsolutePath()); // target directory
+        addJavaAndOSAnswers(test1);
+        test1.addAnswer("y"); // confirm copying
+        test1.addAnswer("n"); // no readme
+        test1.setVerifier(new NormalVerifier());
+        consoleTests.add(test1);
+
+        ConsoleAutotest test2 = new ConsoleAutotest(getOriginalCommandLine());
+        test2.setCommandLineArgs(arguments);
+        test2.addAnswer("e"); // language
+        test2.addAnswer("n"); // no read of license
+        test2.addAnswer("y"); // accept license
+        test2.addAnswer("3"); // type: minimum
+        test2.addAnswer("y"); // include
+        test2.addAnswer("mod"); // include
+        test2.addAnswer("demo"); // include
+        test2.addAnswer("src"); // include
+        test2.addAnswer("n"); // no further includes
+        test2.addAnswer("y"); // exclude
+        test2.addAnswer("demo"); // exclude
+        test2.addAnswer("wrongAnswer"); // wrong answer
+        test2.addAnswer("n"); // no further excludes
+        test2.addAnswer(test2.getTargetDir().getAbsolutePath()); // target directory
+        addJavaAndOSAnswers(test2);
+        test2.addAnswer("y"); // confirm copying
+        test2.addAnswer("n"); // no readme
+        test2.setVerifier(new NormalVerifier());
+        consoleTests.add(test2);
+
+        ConsoleAutotest test3 = new ConsoleAutotest(getOriginalCommandLine());
+        test3.setCommandLineArgs(arguments);
+        test3.addAnswer("e"); // language
+        test3.addAnswer("n"); // no read of license
+        test3.addAnswer("y"); // accept license
+        test3.addAnswer("9"); // type: standalone
+        test3.addAnswer(test3.getTargetDir().getAbsolutePath()); // target directory
+        addJavaAndOSAnswers(test3);
+        test3.addAnswer("y"); // confirm copying
+        test3.addAnswer("n"); // no readme
+        test3.setVerifier(new StandaloneVerifier());
+        consoleTests.add(test3);
+        
+        // test for bug 1783960
+        ConsoleAutotest test4 = new ConsoleAutotest(getOriginalCommandLine());
+        test4.setCommandLineArgs(arguments);
+        test4.addAnswer("e"); // language
+        test4.addAnswer("n"); // no read of license
+        test4.addAnswer("y"); // accept license
+        test4.addAnswer("2"); // type: standard
+        test4.addAnswer("n"); // no includes
+        test4.addAnswer("y"); // exclude
+        test4.addAnswer("n"); // no further excludes
+        test4.addAnswer(test4.getTargetDir().getAbsolutePath()); // target directory
+        addJavaAndOSAnswers(test4);
+        test4.addAnswer("y"); // confirm copying
+        test4.addAnswer("n"); // no readme
+        test4.setVerifier(new NormalVerifier());
+        consoleTests.add(test4);
+        
+        // build array
+        int size = consoleTests.size();
+        _consoleTests = new ConsoleAutotest[size];
+        Iterator<ConsoleAutotest> consoleIterator = consoleTests.iterator();
+        for (int i = 0; i < size; i++) {
+            _consoleTests[i] = consoleIterator.next();
+        }
+    }
+
+    private void addJavaAndOSAnswers(ConsoleAutotest test) {
+        JavaHomeHandler javaHomeHandler = test.getJavaHomeHandler();
+        if (javaHomeHandler.isDeviation() && javaHomeHandler.isValidHome()) {
+            test.addAnswer(javaHomeHandler.getHome().getAbsolutePath()); // different jre
+        } else {
+            test.addAnswer(ConsoleInstaller.CURRENT_JRE);
+        }
+        if (!Installation.isValidOs()) {
+            test.addAnswer(""); // enter to proceed anyway
+        }
+    }
+
+    /**
+     * build all the gui tests
+     * 
+     * @throws IOException
+     * @throws DriverException
+     */
+    private void buildGuiTests() throws IOException, DriverException {
+        List<GuiAutotest> guiTests = new ArrayList<GuiAutotest>(50);
+
+        if (Installation.isGuiAllowed()) {
+            GuiAutotest guiTest1 = new GuiAutotest(getOriginalCommandLine());
+            buildLanguageAndLicensePage(guiTest1);
+            // type page - use 'Standard'
+            guiTest1.addKeyAction(KeyEvent.VK_TAB);
+            guiTest1.addKeyAction(KeyEvent.VK_TAB);
+            guiTest1.addKeyAction(KeyEvent.VK_TAB);
+            guiTest1.addKeyAction(KeyEvent.VK_TAB);
+            guiTest1.addKeyAction(KeyEvent.VK_TAB);
+            guiTest1.addKeyAction(KeyEvent.VK_TAB);
+            guiTest1.addKeyAction(KeyEvent.VK_SPACE);
+            buildRestOfGuiPages(guiTest1);
+            guiTest1.setVerifier(new NormalVerifier());
+            guiTests.add(guiTest1);
+
+            GuiAutotest guiTest2 = new GuiAutotest(getOriginalCommandLine());
+            buildLanguageAndLicensePage(guiTest2);
+            // type page - use 'All'
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_SPACE); // select 'All'
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_TAB);
+            guiTest2.addKeyAction(KeyEvent.VK_SPACE);
+            buildRestOfGuiPages(guiTest2);
+            guiTest2.setVerifier(new NormalVerifier());
+            guiTests.add(guiTest2);
+
+            GuiAutotest guiTest3 = new GuiAutotest(getOriginalCommandLine());
+            buildLanguageAndLicensePage(guiTest3);
+            // type page - use 'Custom'
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_SPACE); // select 'Custom'
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_SPACE); // deselect 'Demos and Examples'
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_SPACE); // deselect 'Documentation'
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_SPACE); // select 'Sources'
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_TAB);
+            guiTest3.addKeyAction(KeyEvent.VK_SPACE);
+            buildRestOfGuiPages(guiTest3);
+            guiTest3.setVerifier(new NormalVerifier());
+            guiTests.add(guiTest3);
+
+            GuiAutotest guiTest4 = new GuiAutotest(getOriginalCommandLine());
+            buildLanguageAndLicensePage(guiTest4);
+            // type page - use 'Standalone'
+            guiTest4.addKeyAction(KeyEvent.VK_TAB);
+            guiTest4.addKeyAction(KeyEvent.VK_TAB);
+            guiTest4.addKeyAction(KeyEvent.VK_SPACE); // select 'Standalone'
+            guiTest4.addKeyAction(KeyEvent.VK_TAB);
+            guiTest4.addKeyAction(KeyEvent.VK_TAB);
+            guiTest4.addKeyAction(KeyEvent.VK_TAB);
+            guiTest4.addKeyAction(KeyEvent.VK_TAB);
+            guiTest4.addKeyAction(KeyEvent.VK_SPACE);
+            buildRestOfGuiPages(guiTest4);
+            guiTest4.setVerifier(new StandaloneVerifier());
+            guiTests.add(guiTest4);
+        }
+
+        // build array
+        int size = guiTests.size();
+        _guiTests = new GuiAutotest[size];
+        Iterator<GuiAutotest> guiIterator = guiTests.iterator();
+        for (int i = 0; i < size; i++) {
+            _guiTests[i] = guiIterator.next();
+        }
+    }
+
+    private void buildLanguageAndLicensePage(GuiAutotest guiTest) {
+        // language page
+        guiTest.addKeyAction(KeyEvent.VK_E);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_SPACE);
+        // license page
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_SPACE); // select "i accept"
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_SPACE);
+    }
+
+    private void buildRestOfGuiPages(GuiAutotest guiTest) {
+        // directory page
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_SPACE);
+        // java selection page
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        guiTest.addKeyAction(KeyEvent.VK_TAB);
+        JavaHomeHandler javaHomeHandler = guiTest.getJavaHomeHandler();
+        boolean isValidDeviation = javaHomeHandler.isDeviation() && javaHomeHandler.isValidHome(); 
+        if (isValidDeviation) { // need 2 more tabs
+            guiTest.addKeyAction(KeyEvent.VK_TAB);
+            guiTest.addKeyAction(KeyEvent.VK_TAB);
+        }
+        guiTest.addKeyAction(KeyEvent.VK_SPACE);
+        // overview page
+        if (isValidDeviation) {
+            guiTest.addKeyAction(KeyEvent.VK_SPACE, 3000); // enough time to check the java version
+        } else {
+            guiTest.addKeyAction(KeyEvent.VK_SPACE);
+        }
+        // installation page (skipped)
+        // readme page
+        guiTest.addWaitingKeyAction(KeyEvent.VK_TAB); // wait for the installation to finish
+        guiTest.addKeyAction(KeyEvent.VK_SPACE);
+        // success page
+        guiTest.addKeyAction(KeyEvent.VK_SPACE);
+    }
+}
diff --git a/installer/src/java/org/python/util/install/driver/NormalVerifier.java b/installer/src/java/org/python/util/install/driver/NormalVerifier.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/NormalVerifier.java
@@ -0,0 +1,287 @@
+package org.python.util.install.driver;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.text.MessageFormat;
+import java.util.StringTokenizer;
+
+import org.python.util.install.ChildProcess;
+import org.python.util.install.FileHelper;
+import org.python.util.install.Installation;
+import org.python.util.install.JavaHomeHandler;
+
+public class NormalVerifier implements Verifier {
+
+    protected static final String AUTOTEST_PY = "autotest.py";
+
+    protected static final String JYTHON_TEST = "jython_test";
+
+    private static final String BIN = "bin";
+
+    private static final String BAT_EXTENSION = ".bat";
+
+    private static final String JYTHON_UP = "jython up and running!";
+
+    private static final String JYTHON = "jython";
+
+    private static final String TEMPLATE_SUFFIX = ".template";
+
+    private static final String VERIFYING = "verifying";
+
+    private File _targetDir;
+
+    public void setTargetDir(File targetDir) {
+        _targetDir = targetDir;
+    }
+
+    public File getTargetDir() {
+        return _targetDir;
+    }
+
+    public void verify() throws DriverException {
+        createTestScriptFile(); // create the test .py script
+        // verify the most simple start of jython works
+        verifyStart(getSimpleCommand());
+        if (doShellScriptTests()) {
+            // verify more complex versions of starting jython
+            verifyStart(getShellScriptTestCommand());
+        }
+    }
+
+    /**
+     * Will be overridden in subclass StandaloneVerifier
+     * 
+     * @return the command array to start jython with
+     * @throws DriverException
+     *             if there was a problem getting the target directory path
+     */
+    protected String[] getSimpleCommand() throws DriverException {
+        String parentDirName = null;
+        try {
+            parentDirName = getTargetDir().getCanonicalPath() + File.separator;
+        } catch (IOException ioe) {
+            throw new DriverException(ioe);
+        }
+        String[] command = new String[2];
+        if (Installation.isWindows()) {
+            command[0] = parentDirName + JYTHON + BAT_EXTENSION;
+        } else {
+            command[0] = parentDirName + JYTHON;
+        }
+        command[1] = parentDirName + AUTOTEST_PY;
+        return command;
+    }
+
+    /**
+     * @return The command to test the shell script more deeply
+     * @throws DriverException
+     */
+    protected final String[] getShellScriptTestCommand() throws DriverException {
+        // first we have to create the shell script
+        File testCommandDir = getShellScriptTestCommandDir();
+        if (!testCommandDir.exists()) {
+            if (!testCommandDir.mkdirs()) {
+                throw new DriverException("unable to create directory "
+                        + testCommandDir.getAbsolutePath());
+            }
+        }
+        String commandName = JYTHON_TEST;
+        boolean isWindows = Installation.isWindows();
+        if (isWindows) {
+            commandName = commandName.concat(BAT_EXTENSION);
+        }
+        File command = new File(testCommandDir, commandName);
+        try {
+            if (!command.exists()) {
+                command.createNewFile();
+            }
+            FileHelper.write(command, getShellScriptTestContents());
+            if (!isWindows) {
+                FileHelper.makeExecutable(command);
+            }
+            return new String[] {command.getCanonicalPath()};
+        } catch (Exception e) {
+            throw new DriverException(e);
+        }
+    }
+
+    /**
+     * @return The contents of the shell test script
+     * @throws DriverException
+     */
+    protected final String getShellScriptTestContents() throws DriverException {
+        String contents = "";
+        String templateName = JYTHON_TEST;
+        if (Installation.isWindows()) {
+            templateName = templateName.concat(BAT_EXTENSION);
+        }
+        templateName = templateName.concat(TEMPLATE_SUFFIX);
+        InputStream inputStream = FileHelper.getRelativeURLAsStream(getClass(), templateName);
+        if (inputStream != null) {
+            try {
+                String template = FileHelper.readAll(inputStream);
+                String targetDirPath = getTargetDir().getCanonicalPath();
+                String upScriptPath = getSimpleCommand()[1];
+                JavaHomeHandler javaHomeHandler = new JavaHomeHandler();
+                String javaHomeString = "";
+                if (javaHomeHandler.isValidHome()) {
+                    javaHomeString = javaHomeHandler.getHome().getAbsolutePath();
+                }
+                contents = MessageFormat.format(template,
+                                                targetDirPath,
+                                                upScriptPath,
+                                                javaHomeString,
+                                                VERIFYING);
+            } catch (Exception e) {
+                throw new DriverException(e);
+            }
+        }
+        return contents;
+    }
+
+    /**
+     * @return The directory where to create the shell script test command in.
+     * 
+     * @throws DriverException
+     */
+    protected final File getShellScriptTestCommandDir() throws DriverException {
+        String dirName;
+        try {
+            dirName = getTargetDir().getCanonicalPath().concat(File.separator).concat(BIN);
+            return new File(dirName);
+        } catch (IOException ioe) {
+            throw new DriverException(ioe);
+        }
+    }
+
+    /**
+     * Internal method verifying a jython-starting command by capturing the ouptut
+     * 
+     * @param command
+     * 
+     * @throws DriverException
+     */
+    private void verifyStart(String[] command) throws DriverException {
+        ChildProcess childProcess = new ChildProcess(command);
+        childProcess.setDebug(true);
+        ByteArrayOutputStream redirectedErr = new ByteArrayOutputStream();
+        ByteArrayOutputStream redirectedOut = new ByteArrayOutputStream();
+        int exitValue = 0;
+        PrintStream oldErr = System.err;
+        PrintStream oldOut = System.out;
+        try {
+            System.setErr(new PrintStream(redirectedErr));
+            System.setOut(new PrintStream(redirectedOut));
+            exitValue = childProcess.run();
+        } finally {
+            System.setErr(oldErr);
+            System.setOut(oldOut);
+        }
+        // verify the output
+        String output = null;
+        String error = null;
+        try {
+            redirectedErr.flush();
+            redirectedOut.flush();
+            String encoding = "US-ASCII";
+            output = redirectedOut.toString(encoding);
+            error = redirectedErr.toString(encoding);
+        } catch (IOException ioe) {
+            throw new DriverException(ioe);
+        }
+        if (exitValue != 0) {
+            throw new DriverException("start of jython failed, output:\n" + output + "\nerror:\n"
+                    + error);
+        }
+        verifyError(error);
+        verifyOutput(output);
+    }
+
+    /**
+     * Will be overridden in subclass StandaloneVerifier
+     * 
+     * @return <code>true</code> if the jython start shell script should be verified (using
+     *         different options)
+     */
+    protected boolean doShellScriptTests() {
+        return true;
+    }
+
+    private void verifyError(String error) throws DriverException {
+        StringTokenizer tokenizer = new StringTokenizer(error, "\n");
+        while (tokenizer.hasMoreTokens()) {
+            String line = tokenizer.nextToken();
+            if (isExpectedError(line)) {
+                feedback(line);
+            } else {
+                throw new DriverException(error);
+            }
+        }
+    }
+
+    private boolean isExpectedError(String line) {
+        boolean expected = false;
+        if (line.startsWith("*sys-package-mgr*")) {
+            expected = true;
+        } else if (line.indexOf("32 bit") >= 0 && line.indexOf("64 bit") >= 0) {
+            // OS X incompatibility message when using -A -j java1.6.0 from java1.5.0
+            expected = true;
+        }
+        return expected;
+    }
+
+    private void verifyOutput(String output) throws DriverException {
+        boolean started = false;
+        StringTokenizer tokenizer = new StringTokenizer(output, "\n");
+        while (tokenizer.hasMoreTokens()) {
+            String line = tokenizer.nextToken();
+            if (isExpectedOutput(line)) {
+                feedback(line);
+                if (line.startsWith(JYTHON_UP)) {
+                    started = true;
+                }
+            } else {
+                throw new DriverException(output);
+            }
+        }
+        if (!started) {
+            throw new DriverException("start of jython failed:\n" + output);
+        }
+    }
+
+    private boolean isExpectedOutput(String line) {
+        boolean expected = false;
+        if (line.startsWith("[ChildProcess]") || line.startsWith(VERIFYING)) {
+            expected = true;
+        } else if (line.startsWith(JYTHON_UP)) {
+            expected = true;
+        }
+        return expected;
+    }
+
+    private String getTestScript() {
+        StringBuilder b = new StringBuilder(80);
+        b.append("import sys\n");
+        b.append("import os\n");
+        b.append("print '");
+        b.append(JYTHON_UP);
+        b.append("'\n");
+        return b.toString();
+    }
+
+    private void createTestScriptFile() throws DriverException {
+        File file = new File(getTargetDir(), AUTOTEST_PY);
+        try {
+            FileHelper.write(file, getTestScript());
+        } catch (IOException ioe) {
+            throw new DriverException(ioe);
+        }
+    }
+
+    private void feedback(String line) {
+        System.out.println(line);
+    }
+}
diff --git a/installer/src/java/org/python/util/install/driver/SilentAutotest.java b/installer/src/java/org/python/util/install/driver/SilentAutotest.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/SilentAutotest.java
@@ -0,0 +1,25 @@
+package org.python.util.install.driver;
+
+import java.io.IOException;
+
+import org.python.util.install.InstallerCommandLine;
+
+public class SilentAutotest extends Autotest {
+
+    protected SilentAutotest(InstallerCommandLine commandLine) throws IOException, DriverException {
+        super(commandLine);
+    }
+
+    protected String getNameSuffix() {
+        return "silentTest";
+    }
+
+    //
+    // interface InstallationListener
+    //
+
+    public void progressFinished() {
+        // ignored
+    }
+
+}
diff --git a/installer/src/java/org/python/util/install/driver/StandaloneVerifier.java b/installer/src/java/org/python/util/install/driver/StandaloneVerifier.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/StandaloneVerifier.java
@@ -0,0 +1,74 @@
+package org.python.util.install.driver;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import org.python.util.install.JavaHomeHandler;
+
+public class StandaloneVerifier extends NormalVerifier {
+
+    public void verify() throws DriverException {
+        // make sure only JYTHON_JAR is in the target directory
+        if (getTargetDir().listFiles().length > 1) {
+            throw new DriverException("more than " + JYTHON_JAR + " installed");
+        }
+        // make sure JYTHON_JAR contains a MANIFEST and a /Lib directory
+        verifyJythonJar();
+        // do the jython startup verification from the superclass
+        super.verify();
+    }
+
+    @Override
+    protected String[] getSimpleCommand() throws DriverException {
+        String parentDirName = null;
+        try {
+            parentDirName = getTargetDir().getCanonicalPath() + File.separator;
+        } catch (IOException ioe) {
+            throw new DriverException(ioe);
+        }
+        String command[] = new String[4];
+        command[0] = new JavaHomeHandler().getExecutableName();
+        command[1] = "-jar";
+        command[2] = parentDirName + JYTHON_JAR;
+        command[3] = parentDirName + AUTOTEST_PY;
+        return command;
+    }
+
+    @Override
+    protected boolean doShellScriptTests() {
+        return false;
+    }
+
+    private void verifyJythonJar() throws DriverException {
+        File jythonJar = getTargetDir().listFiles()[0];
+        JarFile jar = null;
+        try {
+            jar = new JarFile(jythonJar);
+            if (jar.getManifest() == null) {
+                throw new DriverException(JYTHON_JAR + " contains no MANIFEST");
+            }
+            boolean hasLibDir = false;
+            Enumeration<JarEntry> entries = jar.entries();
+            while (!hasLibDir && entries.hasMoreElements()) {
+                JarEntry entry = (JarEntry)entries.nextElement();
+                if (entry.getName().startsWith("Lib/")) {
+                    hasLibDir = true;
+                }
+            }
+            if (!hasLibDir) {
+                throw new DriverException(JYTHON_JAR + " contains no /Lib directory");
+            }
+        } catch (IOException e) {
+            throw new DriverException(e);
+        } finally {
+            if (jar != null) {
+                try {
+                    jar.close();
+                } catch (IOException ioe) {}
+            }
+        }
+    }
+}
diff --git a/installer/src/java/org/python/util/install/driver/Tunnel.java b/installer/src/java/org/python/util/install/driver/Tunnel.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/Tunnel.java
@@ -0,0 +1,61 @@
+package org.python.util.install.driver;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+/**
+ * A communication tunnel between a console driver and a console.
+ * 
+ * <pre>
+ *   (2)  [Driver]   receives question  [Tunnel]   sends question   [Console]  (1)
+ *   (3)  [Driver]   sends answer       [Tunnel]   receives answer  [Console]  (4)
+ * </pre>
+ */
+public class Tunnel {
+
+    public static final String NEW_LINE = "\n";
+
+    private PipedOutputStream _questionSenderStream;
+    private PipedInputStream _questionReceiverStream;
+    private PipedOutputStream _answerSenderStream;
+    private PipedInputStream _answerReceiverStream;
+
+    public Tunnel() throws IOException {
+        _questionSenderStream = new PipedOutputStream();
+        _questionReceiverStream = new PipedInputStream();
+        _questionSenderStream.connect(_questionReceiverStream);
+
+        _answerSenderStream = new PipedOutputStream();
+        _answerReceiverStream = new PipedInputStream();
+        _answerSenderStream.connect(_answerReceiverStream);
+    }
+
+    public PipedOutputStream getQuestionSenderStream() {
+        return _questionSenderStream;
+    }
+
+    public PipedInputStream getQuestionReceiverStream() {
+        return _questionReceiverStream;
+    }
+
+    public PipedOutputStream getAnswerSenderStream() {
+        return _answerSenderStream;
+    }
+
+    public PipedInputStream getAnswerReceiverStream() {
+        return _answerReceiverStream;
+    }
+
+    public void close() throws IOException {
+        _questionReceiverStream.close();
+        _questionSenderStream.close();
+        _answerReceiverStream.close();
+        _answerSenderStream.close();
+
+        _questionReceiverStream = null;
+        _questionSenderStream = null;
+        _answerReceiverStream = null;
+        _answerSenderStream = null;
+    }
+}
diff --git a/installer/src/java/org/python/util/install/driver/Verifier.java b/installer/src/java/org/python/util/install/driver/Verifier.java
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/Verifier.java
@@ -0,0 +1,17 @@
+package org.python.util.install.driver;
+
+import java.io.File;
+
+import org.python.util.install.JarInstaller;
+
+public interface Verifier {
+
+    public static final String JYTHON_JAR = JarInstaller.JYTHON_JAR;
+
+    public void setTargetDir(File targetDir);
+
+    public File getTargetDir();
+
+    public void verify() throws DriverException;
+
+}
diff --git a/installer/src/java/org/python/util/install/driver/jython_test.bat.template b/installer/src/java/org/python/util/install/driver/jython_test.bat.template
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/jython_test.bat.template
@@ -0,0 +1,73 @@
+ at echo off
+
+rem 3 variables to be set from the caller, UNquoted:
+set _INSTALL_DIR={0}
+set _SCRIPT={1}
+set _JAVA_HOME={2}
+
+rem save old home env vars and classpath:
+set _OLD_JAVA_HOME=%JAVA_HOME%
+set _OLD_JYTHON_HOME=%JYTHON_HOME%
+set _OLD_CLASSPATH=%CLASSPATH%
+
+cd /d "%_INSTALL_DIR%\bin"
+
+echo {3}: only JAVA_HOME, quoted:
+set JAVA_HOME="%_JAVA_HOME%"
+set JYTHON_HOME=
+call jython.bat "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+echo {3}: only JAVA_HOME, UNquoted:
+set JAVA_HOME=%_JAVA_HOME%
+set JYTHON_HOME=
+call jython.bat "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+echo {3}: both homes quoted:
+set JAVA_HOME="%_JAVA_HOME%"
+set JYTHON_HOME="%_INSTALL_DIR%"
+call jython.bat "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+echo {3}: both homes UNquoted:
+set JAVA_HOME=%_JAVA_HOME%
+set JYTHON_HOME=%_INSTALL_DIR%
+call jython.bat "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+cd ..
+
+echo {3}: no home, calling in home dir:
+set JAVA_HOME=
+set JYTHON_HOME=
+call jython.bat "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+cd ..
+
+echo {3}: no home, calling /jython.bat from another working dir:"
+set JAVA_HOME=
+set JYTHON_HOME=
+call "%_INSTALL_DIR%\jython.bat" "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+echo {3}: no home, calling bin/jython.bat from another working dir:"
+set JAVA_HOME=
+set JYTHON_HOME=
+call "%_INSTALL_DIR%\bin\jython.bat" "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+echo {3}: no home, setting CLASSPATH, calling /jython.bat from another working dir:"
+set JAVA_HOME=
+set JYTHON_HOME=
+set CLASSPATH=.;C:\Program Files (x86)\Java\jre6\lib\ext\QTJava.zip
+call "%_INSTALL_DIR%\jython.bat" "%_SCRIPT%"
+set E=%ERRORLEVEL%
+
+rem cleanup:
+set JAVA_HOME=%_OLD_JAVA_HOME%
+set JYTHON_HOME=%_OLD_JYTHON_HOME%
+set CLASSPATH=%_OLD_CLASSPATH%
+cd /d "%~dp0%"
+exit /b %E%
diff --git a/installer/src/java/org/python/util/install/driver/jython_test.template b/installer/src/java/org/python/util/install/driver/jython_test.template
new file mode 100644
--- /dev/null
+++ b/installer/src/java/org/python/util/install/driver/jython_test.template
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+
+# 3 variables to be set from the caller (unquoted) -> quoted in here:
+_INSTALL_DIR="{0}"
+_SCRIPT="{1}"
+_JAVA_HOME="{2}"
+
+# save old home env vars:
+_OLD_JAVA_HOME=$JAVA_HOME
+_OLD_JYTHON_HOME=$JYTHON_HOME
+# save current dir
+_CURDIR=`pwd`
+
+cd "$_INSTALL_DIR/bin"
+
+echo "{3}: no home:"
+export JAVA_HOME=
+export JYTHON_HOME=
+./jython "$_SCRIPT"
+
+echo "{3}: only JAVA_HOME:"
+export JAVA_HOME="$_JAVA_HOME"
+export JYTHON_HOME=
+./jython "$_SCRIPT"
+
+echo "{3}: only JYTHON_HOME:"
+export JAVA_HOME=
+export JYTHON_HOME="$_INSTALL_DIR"
+./jython "$_SCRIPT"
+
+echo "{3}: both homes:"
+export JAVA_HOME="$_JAVA_HOME"
+export JYTHON_HOME="$_INSTALL_DIR"
+./jython "$_SCRIPT"
+
+cd ..
+
+echo "{3}: no home, calling in home dir:"
+export JAVA_HOME=
+export JYTHON_HOME=
+./jython "$_SCRIPT"
+
+cd ~
+
+echo "{3}: no home, calling /jython from another working dir:"
+export JAVA_HOME=
+export JYTHON_HOME=
+"$_INSTALL_DIR/jython" "$_SCRIPT"
+
+echo "{3}: no home, calling /bin/jython from another working dir:"
+export JAVA_HOME=
+export JYTHON_HOME=
+"$_INSTALL_DIR/bin/jython" "$_SCRIPT"
+
+# cleanup:
+cd "$_CURDIR"
+export JAVA_HOME=$_OLD_JAVA_HOME
+export JYTHON_HOME=$_OLD_JYTHON_HOME
diff --git a/installer/src/java/org/python/util/install/jython_small_c.png b/installer/src/java/org/python/util/install/jython_small_c.png
new file mode 100644
index 0000000000000000000000000000000000000000..e65a7f18e5ecad694dd2093cf01256a2eb93aada
GIT binary patch
[stripped]
diff --git a/installer/test/java/org/AllTests.java b/installer/test/java/org/AllTests.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/AllTests.java
@@ -0,0 +1,101 @@
+package org;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * A package recursive test suite.
+ * <p>
+ * All classes ending with 'Test' are added to the suite.
+ * 
+ * @see AllTests.TestClassFilter
+ */
+public class AllTests extends TestSuite {
+
+    /**
+     * @return Test suite at the directory where this class resists
+     * 
+     * @throws Exception
+     */
+    public static Test suite() throws Exception {
+        Class<AllTests> suiteClass = AllTests.class;
+        String testSuiteClassName = suiteClass.getName();
+        File suiteFile = new File(suiteClass.getClassLoader().getResource(
+                testSuiteClassName.replace('.', '/').concat(".class")).getFile());
+        String basePackage = suiteClass.getPackage().getName();
+        File baseDir = suiteFile.getParentFile();
+        TestSuite suite = new TestSuite("Test " + basePackage + " recursive.");
+        buildSuite(baseDir.getAbsolutePath().length(), basePackage, baseDir, new TestClassFilter(), suite);
+        return suite;
+    }
+
+    //
+    // private methods
+    //
+
+    private static void buildSuite(int prefixLength, String basePackage, File currentDir, FilenameFilter filter,
+            TestSuite currentSuite) throws Exception {
+        List<File> potentialDirectories = Arrays.asList(currentDir.listFiles(filter));
+        if (potentialDirectories.size() == 0) {
+            return;
+        }
+        StringBuffer currentPackageName = new StringBuffer(200);
+        currentPackageName.append(basePackage);
+        currentPackageName.append(currentDir.getAbsolutePath().substring(prefixLength).replace('\\', '.').replace('/',
+                '.'));
+
+        List<File> classFiles = new ArrayList<File>(potentialDirectories.size());
+        Collections.sort(potentialDirectories, new FileComparator());
+        Iterator<File> directoryIterator = potentialDirectories.iterator();
+        while (directoryIterator.hasNext()) {
+            File potentialDirectory = (File) directoryIterator.next();
+            if (potentialDirectory.isDirectory()) {
+                TestSuite subTestSuite = new TestSuite(potentialDirectory.getName());
+                buildSuite(prefixLength, basePackage, potentialDirectory, filter, subTestSuite);
+                // only if suite contains tests
+                if (subTestSuite.countTestCases() > 0) {
+                    currentSuite.addTest(subTestSuite);
+                }
+            } else {
+                classFiles.add(potentialDirectory);
+            }
+        }
+        Iterator<File> fileIterator = classFiles.iterator();
+        while (fileIterator.hasNext()) {
+            File file = (File) fileIterator.next();
+            StringBuffer className = new StringBuffer(200);
+            className.append(currentPackageName);
+            className.append('.');
+            String fileName = file.getName();
+            className.append(fileName);
+            className.setLength(className.length() - 6);
+            currentSuite.addTest(new TestSuite(Class.forName(className.toString()), fileName.substring(0, fileName
+                    .length() - 6)));
+        }
+    }
+
+    private static class TestClassFilter implements FilenameFilter {
+        public boolean accept(File dir, String name) {
+            if (name.endsWith("Test.class")) {
+                return true;
+            }
+            return new File(dir, name).isDirectory();
+        }
+    }
+
+    private static class FileComparator implements Comparator<File> {
+        public int compare(File f1, File f2) {
+            return f1.getAbsolutePath().compareTo(f2.getAbsolutePath());
+        }
+    }
+    
+}
diff --git a/installer/test/java/org/apache/commons/cli/ApplicationTest.java b/installer/test/java/org/apache/commons/cli/ApplicationTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/ApplicationTest.java
@@ -0,0 +1,120 @@
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * <p>
+ * This is a collection of tests that test real world
+ * applications command lines.
+ * </p>
+ * 
+ * <p>
+ * The following are the applications that are tested:
+ * <ul>
+ * <li>Ant</li>
+ * </ul>
+ * </p>
+ *
+ * @author John Keyes (john at integralsource.com)
+ */
+public class ApplicationTest extends TestCase {
+
+    public static Test suite() { 
+        return new TestSuite(ApplicationTest.class); 
+    }
+
+    public ApplicationTest(String name)
+    {
+        super(name);
+    }
+    
+    /**
+     *	
+     */
+    public void testLs() {
+        // create the command line parser
+        CommandLineParser parser = new PosixParser();
+        Options options = new Options();
+        options.addOption( "a", "all", false, "do not hide entries starting with ." );
+        options.addOption( "A", "almost-all", false, "do not list implied . and .." );
+        options.addOption( "b", "escape", false, "print octal escapes for nongraphic characters" );
+        OptionBuilder.withLongOpt( "block-size" );
+        OptionBuilder.withDescription( "use SIZE-byte blocks" );
+        OptionBuilder.withValueSeparator( '=' );
+        OptionBuilder.hasArg();
+        options.addOption(OptionBuilder.create());
+//        options.addOption( OptionBuilder.withLongOpt( "block-size" )
+//                .withDescription( "use SIZE-byte blocks" )
+//                .withValueSeparator( '=' )
+//                .hasArg()
+//                .create() );
+        options.addOption( "B", "ignore-backups", false, "do not list implied entried ending with ~");
+        options.addOption( "c", false, "with -lt: sort by, and show, ctime (time of last modification of file status information) with -l:show ctime and sort by name otherwise: sort by ctime" );
+        options.addOption( "C", false, "list entries by columns" );
+
+        String[] args = new String[]{ "--block-size=10" };
+
+        try {
+            CommandLine line = parser.parse( options, args );
+            assertTrue( line.hasOption( "block-size" ) );
+            assertEquals( line.getOptionValue( "block-size" ), "10" );
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected exception:" + exp.getMessage() );
+        }
+    }
+
+    /**
+     * Ant test
+     */
+    public void testAnt() {
+        // use the GNU parser
+        CommandLineParser parser = new GnuParser( );
+        Options options = new Options();
+        options.addOption( "help", false, "print this message" );
+        options.addOption( "projecthelp", false, "print project help information" );
+        options.addOption( "version", false, "print the version information and exit" );
+        options.addOption( "quiet", false, "be extra quiet" );
+        options.addOption( "verbose", false, "be extra verbose" );
+        options.addOption( "debug", false, "print debug information" );
+        options.addOption( "version", false, "produce logging information without adornments" );
+        options.addOption( "logfile", true, "use given file for log" );
+        options.addOption( "logger", true, "the class which is to perform the logging" );
+        options.addOption( "listener", true, "add an instance of a class as a project listener" );
+        options.addOption( "buildfile", true, "use given buildfile" );
+        OptionBuilder.withDescription( "use value for given property" );
+        OptionBuilder.hasArgs();
+        OptionBuilder.withValueSeparator();
+        options.addOption( OptionBuilder.create( 'D' ) );
+                           //, null, true, , false, true );
+        options.addOption( "find", true, "search for buildfile towards the root of the filesystem and use it" );
+
+        String[] args = new String[]{ "-buildfile", "mybuild.xml",
+            "-Dproperty=value", "-Dproperty1=value1",
+            "-projecthelp" };
+
+        try {
+            CommandLine line = parser.parse( options, args );
+
+            // check multiple values
+            String[] opts = line.getOptionValues( "D" );
+            assertEquals( "property", opts[0] );
+            assertEquals( "value", opts[1] );
+            assertEquals( "property1", opts[2] );
+            assertEquals( "value1", opts[3] );
+
+            // check single value
+            assertEquals( line.getOptionValue( "buildfile"), "mybuild.xml" );
+
+            // check option
+            assertTrue( line.hasOption( "projecthelp") );
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected exception:" + exp.getMessage() );
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/installer/test/java/org/apache/commons/cli/BugsTest.java b/installer/test/java/org/apache/commons/cli/BugsTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/BugsTest.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: BugsTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class BugsTest extends TestCase
+{
+
+    public static Test suite() { 
+        return new TestSuite( BugsTest.class );
+    }
+
+    public BugsTest( String name )
+    {
+        super( name );
+    }
+
+    public void setUp()
+    {
+    }
+
+    public void tearDown()
+    {
+    }
+
+    public void test11457() {
+        Options options = new Options();
+        OptionBuilder.withLongOpt( "verbose" );
+        options.addOption( OptionBuilder.create() );
+        String[] args = new String[] { "--verbose" };
+
+        CommandLineParser parser = new PosixParser();
+
+        try {
+            CommandLine cmd = parser.parse( options, args );
+            assertTrue( cmd.hasOption( "verbose" ) );
+        }        
+        catch( ParseException exp ) {
+            exp.printStackTrace();
+            fail( "Unexpected Exception: " + exp.getMessage() );
+        }
+    }
+
+    public void test11458()
+    {
+        Options options = new Options();
+        OptionBuilder.withValueSeparator( '=' );
+        OptionBuilder.hasArgs();
+        options.addOption(OptionBuilder.create( 'D' ) );
+        OptionBuilder.withValueSeparator( ':' );
+        OptionBuilder.hasArgs();
+        options.addOption( OptionBuilder.create( 'p' ) );
+        String[] args = new String[] { "-DJAVA_HOME=/opt/java" ,
+        "-pfile1:file2:file3" };
+
+        CommandLineParser parser = new PosixParser();
+
+        try {
+            CommandLine cmd = parser.parse( options, args );
+
+            String[] values = cmd.getOptionValues( 'D' );
+
+            assertEquals( values[0], "JAVA_HOME" );
+            assertEquals( values[1], "/opt/java" );
+
+            values = cmd.getOptionValues( 'p' );
+
+            assertEquals( values[0], "file1" );
+            assertEquals( values[1], "file2" );
+            assertEquals( values[2], "file3" );
+
+            java.util.Iterator iter = cmd.iterator();
+            while( iter.hasNext() ) {
+                Option opt = (Option)iter.next();
+                switch( opt.getId() ) {
+                    case 'D':
+                        assertEquals( opt.getValue( 0 ), "JAVA_HOME" );
+                        assertEquals( opt.getValue( 1 ), "/opt/java" );
+                        break;
+                    case 'p':
+                        assertEquals( opt.getValue( 0 ), "file1" );
+                        assertEquals( opt.getValue( 1 ), "file2" );
+                        assertEquals( opt.getValue( 2 ), "file3" );
+                        break;
+                    default:
+                        fail( "-D option not found" );
+                }
+            }
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected Exception:\nMessage:" + exp.getMessage() 
+                  + "Type: " + exp.getClass().getName() );
+        }
+    }
+
+    public void test11680()
+    {
+        Options options = new Options();
+        options.addOption("f", true, "foobar");
+	options.addOption("m", true, "missing");
+        String[] args = new String[] { "-f" , "foo" };
+
+        CommandLineParser parser = new PosixParser();
+
+        try {
+            CommandLine cmd = parser.parse( options, args );
+
+            try {
+                cmd.getOptionValue( "f", "default f");
+                cmd.getOptionValue( "m", "default m");
+            }
+            catch( NullPointerException exp ) {
+                fail( "NullPointer caught: " + exp.getMessage() );
+            }
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected Exception: " + exp.getMessage() );
+        }
+    }
+
+    public void test11456()
+    {
+        // Posix 
+        Options options = new Options();
+        OptionBuilder.hasOptionalArg();
+        options.addOption( OptionBuilder.create( 'a' ) );
+        OptionBuilder.hasArg();
+        options.addOption( OptionBuilder.create( 'b' ) );
+        String[] args = new String[] { "-a", "-bvalue" };
+
+        CommandLineParser parser = new PosixParser();
+
+        try {
+            CommandLine cmd = parser.parse( options, args );
+            assertEquals( cmd.getOptionValue( 'b' ), "value" );
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected Exception: " + exp.getMessage() );
+        }
+
+        // GNU
+        options = new Options();
+        OptionBuilder.hasOptionalArg();
+        options.addOption( OptionBuilder.create( 'a' ) );
+        OptionBuilder.hasArg();
+        options.addOption( OptionBuilder.create( 'b' ) );
+        args = new String[] { "-a", "-b", "value" };
+
+        parser = new GnuParser();
+
+        try {
+            CommandLine cmd = parser.parse( options, args );
+            assertEquals( cmd.getOptionValue( 'b' ), "value" );
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected Exception: " + exp.getMessage() );
+        }
+
+    }
+
+    public void test12210() {
+        // create the main options object which will handle the first parameter
+        Options mainOptions = new Options();
+        // There can be 2 main exclusive options:  -exec|-rep
+
+        // Therefore, place them in an option group
+
+        String[] argv = new String[] { "-exec", "-exec_opt1", "-exec_opt2" };
+        OptionGroup grp = new OptionGroup();
+
+        grp.addOption(new Option("exec",false,"description for this option"));
+
+        grp.addOption(new Option("rep",false,"description for this option"));
+
+        mainOptions.addOptionGroup(grp);
+
+        // for the exec option, there are 2 options...
+        Options execOptions = new Options();
+        execOptions.addOption("exec_opt1",false," desc");
+        execOptions.addOption("exec_opt2",false," desc");
+
+        // similarly, for rep there are 2 options...
+        Options repOptions = new Options();
+        repOptions.addOption("repopto",false,"desc");
+        repOptions.addOption("repoptt",false,"desc");
+
+        // create the parser
+        GnuParser parser = new GnuParser();
+
+        // finally, parse the arguments:
+
+        // first parse the main options to see what the user has specified
+        // We set stopAtNonOption to true so it does not touch the remaining
+        // options
+        try {
+            CommandLine cmd = parser.parse(mainOptions,argv,true);
+            // get the remaining options...
+            argv = cmd.getArgs();
+
+            if(cmd.hasOption("exec")){
+                cmd = parser.parse(execOptions,argv,false);
+                // process the exec_op1 and exec_opt2...
+                assertTrue( cmd.hasOption("exec_opt1") );
+                assertTrue( cmd.hasOption("exec_opt2") );
+            }
+            else if(cmd.hasOption("rep")){
+                cmd = parser.parse(repOptions,argv,false);
+                // process the rep_op1 and rep_opt2...
+            }
+            else {
+                fail( "exec option not found" );
+            }
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected exception: " + exp.getMessage() );
+        }
+    }
+
+    public void test13425() {
+        Options options = new Options();
+        OptionBuilder.withLongOpt( "old-password" );
+        OptionBuilder.withDescription( "Use this option to specify the old password" );
+        OptionBuilder.hasArg();
+        Option oldpass = OptionBuilder.create( 'o' );
+        OptionBuilder.withLongOpt( "new-password" );
+        OptionBuilder.withDescription( "Use this option to specify the new password" );
+        OptionBuilder.hasArg();
+        Option newpass = OptionBuilder.create( 'n' );
+
+        String[] args = { 
+            "-o", 
+            "-n", 
+            "newpassword" 
+        };
+
+        options.addOption( oldpass );
+        options.addOption( newpass );
+
+        Parser parser = new PosixParser();
+
+        CommandLine line = null;
+        try {
+            line = parser.parse( options, args );
+        }
+        // catch the exception and leave the method
+        catch( Exception exp ) {
+            assertTrue( exp != null );
+            return;
+        }
+        fail( "MissingArgumentException not caught." + line);
+    }
+
+    public void test13666() {
+        Options options = new Options();
+        OptionBuilder.withDescription( "dir" );
+        OptionBuilder.hasArg();
+        Option dir = OptionBuilder.create( 'd' );
+        options.addOption( dir );
+        try {
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp( "dir", options );
+        }
+        catch( Exception exp ) {
+            fail( "Unexpected Exception: " + exp.getMessage() );
+        }
+    }
+
+    public void test13935() {
+        OptionGroup directions = new OptionGroup();
+
+        Option left = new Option( "l", "left", false, "go left" );
+        Option right = new Option( "r", "right", false, "go right" );
+        Option straight = new Option( "s", "straight", false, "go straight" );
+        Option forward = new Option( "f", "forward", false, "go forward" );
+        forward.setRequired( true );
+
+        directions.addOption( left );
+        directions.addOption( right );
+        directions.setRequired( true );
+
+        Options opts = new Options();
+        opts.addOptionGroup( directions );
+        opts.addOption( straight );
+
+        CommandLineParser parser = new PosixParser();
+        boolean exception = false;
+
+        CommandLine line = null;
+        String[] args = new String[] {  };
+        try {
+            line = parser.parse( opts, args );
+        }
+        catch( ParseException exp ) {
+            exception = true;
+        }
+
+        if( !exception ) {
+            fail( "Expected exception not caught.");
+        }
+
+        exception = false;
+
+        args = new String[] { "-s" };
+        try {
+            line = parser.parse( opts, args );
+        }
+        catch( ParseException exp ) {
+            exception = true;
+        }
+
+        if( !exception ) {
+            fail( "Expected exception not caught.");
+        }
+
+        exception = false;
+
+        args = new String[] { "-s", "-l" };
+        try {
+            line = parser.parse( opts, args );
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected exception: " + exp.getMessage() );
+        }
+
+        opts.addOption( forward );
+        args = new String[] { "-s", "-l", "-f" };
+        try {
+            line = parser.parse( opts, args );
+        }
+        catch( ParseException exp ) {
+            fail( "Unexpected exception: " + exp.getMessage() );
+        }
+        if(line != null) {
+            line = null;
+        }
+    }
+}
diff --git a/installer/test/java/org/apache/commons/cli/BuildTest.java b/installer/test/java/org/apache/commons/cli/BuildTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/BuildTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: BuildTest.java 2662 2006-02-18 14:20:33Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class BuildTest extends TestCase
+{
+
+    public static Test suite() { 
+        return new TestSuite(BuildTest.class); 
+    }
+
+    public BuildTest(String name)
+    {
+        super(name);
+    }
+
+    public void setUp()
+    {
+
+    }
+
+    public void tearDown()
+    {
+
+    }
+
+    public void testSimple()
+    {
+        Options opts = new Options();
+        
+        opts.addOption("a",
+                       false,
+                       "toggle -a");
+
+        opts.addOption("b",
+                       true,
+                       "toggle -b");
+    }
+
+    public void testDuplicateSimple()
+    {
+        Options opts = new Options();
+        opts.addOption("a",
+                       false,
+                       "toggle -a");
+
+        opts.addOption("a",
+                       true,
+                       "toggle -a*");
+        
+        assertEquals( "last one in wins", "toggle -a*", opts.getOption("a").getDescription() );
+    }
+
+    public void testLong()
+    {
+        Options opts = new Options();
+        
+        opts.addOption("a",
+                       "--a",
+                       false,
+                       "toggle -a");
+
+        opts.addOption("b",
+                       "--b",
+                       true,
+                       "set -b");
+
+    }
+
+    public void testDuplicateLong()
+    {
+        Options opts = new Options();
+        opts.addOption("a",
+                       "--a",
+                       false,
+                       "toggle -a");
+
+        opts.addOption("a",
+                       "--a",
+                       false,
+                       "toggle -a*");
+        assertEquals( "last one in wins", "toggle -a*", opts.getOption("a").getDescription() );
+    }
+}
diff --git a/installer/test/java/org/apache/commons/cli/GnuParseTest.java b/installer/test/java/org/apache/commons/cli/GnuParseTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/GnuParseTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: GnuParseTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class GnuParseTest extends TestCase
+{
+    private Options _options = null;
+    private CommandLineParser _parser = null;
+
+    public static Test suite() { 
+        return new TestSuite( GnuParseTest.class ); 
+    }
+
+    public GnuParseTest( String name )
+    {
+        super( name );
+    }
+
+    public void setUp()
+    {
+        _options = new Options()
+            .addOption("a",
+                       "enable-a",
+                       false,
+                       "turn [a] on or off")
+            .addOption("b",
+                       "bfile",
+                       true,
+                       "set the value of [b]")
+            .addOption("c",
+                       "copt",
+                       false,
+                       "turn [c] on or off");
+
+        _parser = new GnuParser( );
+    }
+
+    public void tearDown()
+    {
+
+    }
+
+    public void testSimpleShort()
+    {
+        String[] args = new String[] { "-a",
+                                       "-b", "toast",
+                                       "foo", "bar" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+            
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm size of extra args", cl.getArgList().size() == 2);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testSimpleLong()
+    {
+        String[] args = new String[] { "--enable-a",
+                                       "--bfile", "toast",
+                                       "foo", "bar" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+            
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm size of extra args", cl.getArgList().size() == 2);
+        } 
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testExtraOption()
+    {
+        String[] args = new String[] { "-a", "-d", "-b", "toast",
+                                       "foo", "bar" };
+
+        boolean caught = false;
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+            
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm size of extra args", cl.getArgList().size() == 3);
+        }
+        catch (UnrecognizedOptionException e)
+        {
+            caught = true;
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+        assertTrue( "Confirm UnrecognizedOptionException caught", caught );
+    }
+
+    public void testMissingArg()
+    {
+
+        String[] args = new String[] { "-b" };
+
+        boolean caught = false;
+
+        CommandLine cl = null;
+        try
+        {
+            cl = _parser.parse(_options, args);
+        }
+        catch (MissingArgumentException e)
+        {
+            caught = true;
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+
+        assertTrue( "Confirm MissingArgumentException caught " + cl, caught );
+    }
+
+    public void testStop()
+    {
+        String[] args = new String[] { "-c",
+                                       "foober",
+                                       "-b",
+                                       "toast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args, true);
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm  3 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 3);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testMultiple()
+    {
+        String[] args = new String[] { "-c",
+                                       "foobar",
+                                       "-b",
+                                       "toast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args, true);
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm  3 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 3);
+
+            cl = _parser.parse(_options, cl.getArgs() );
+
+            assertTrue( "Confirm -c is not set", ! cl.hasOption("c") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm  1 extra arg: " + cl.getArgList().size(), cl.getArgList().size() == 1);
+            assertTrue( "Confirm  value of extra arg: " + cl.getArgList().get(0), cl.getArgList().get(0).equals("foobar") );
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testMultipleWithLong()
+    {
+        String[] args = new String[] { "--copt",
+                                       "foobar",
+                                       "--bfile", "toast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options,args,
+                                            true);
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm  3 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 3);
+
+            cl = _parser.parse(_options, cl.getArgs() );
+
+            assertTrue( "Confirm -c is not set", ! cl.hasOption("c") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm  1 extra arg: " + cl.getArgList().size(), cl.getArgList().size() == 1);
+            assertTrue( "Confirm  value of extra arg: " + cl.getArgList().get(0), cl.getArgList().get(0).equals("foobar") );
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testDoubleDash()
+    {
+        String[] args = new String[] { "--copt",
+                                       "--",
+                                       "-b", "toast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm -b is not set", ! cl.hasOption("b") );
+            assertTrue( "Confirm 2 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 2);
+
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testSingleDash()
+    {
+        String[] args = new String[] { "--copt",
+                                       "-b", "-",
+                                       "-a",
+                                       "-" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("-") );
+            assertTrue( "Confirm 1 extra arg: " + cl.getArgList().size(), cl.getArgList().size() == 1);
+            assertTrue( "Confirm value of extra arg: " + cl.getArgList().get(0), cl.getArgList().get(0).equals("-") );
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+        
+    }
+}
diff --git a/installer/test/java/org/apache/commons/cli/HelpFormatterExamples.java b/installer/test/java/org/apache/commons/cli/HelpFormatterExamples.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/HelpFormatterExamples.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: HelpFormatterExamples.java 2662 2006-02-18 14:20:33Z otmarhumbel $
+ */
+package org.apache.commons.cli;
+
+/** 
+ * A sample program shpwing the use of Options and the HelpFormatter class 
+ *
+ * @author Slawek Zachcial
+ **/
+public class HelpFormatterExamples
+{
+   // --------------------------------------------------------------- Constants
+
+   // ------------------------------------------------------------------ Static
+
+   public static void main( String[] args )
+   {
+      System.out.println("\n#\n# 'man' example\n#");
+      manExample();
+/*
+      System.out.println("\n#\n# 'bzip2' example\n#");
+      bzip2Example();
+      System.out.println("\n#\n# 'ls' example\n#");
+      lsExample();
+*/
+   }
+
+   static void manExample()
+   {
+      String cmdLine =
+         "man [-c|-f|-k|-w|-tZT device] [-adlhu7V] [-Mpath] [-Ppager] [-Slist] " +
+         "[-msystem] [-pstring] [-Llocale] [-eextension] [section] page ...";
+      Options opts =
+         new Options().
+         addOption("a", "all",            false, "find all matching manual pages.").
+         addOption("d", "debug",          false, "emit debugging messages.").
+         addOption("e", "extension",      false, "limit search to extension type 'extension'.").
+         addOption("f", "whatis",         false, "equivalent to whatis.").
+         addOption("k", "apropos",        false, "equivalent to apropos.").
+         addOption("w", "location",       false, "print physical location of man page(s).").
+         addOption("l", "local-file",     false, "interpret 'page' argument(s) as local filename(s)").
+         addOption("u", "update",         false, "force a cache consistency check.").
+         //FIXME - should generate -r,--prompt string
+         addOption("r", "prompt",         true,  "provide 'less' pager with prompt.").
+         addOption("c", "catman",         false, "used by catman to reformat out of date cat pages.").
+         addOption("7", "ascii",          false, "display ASCII translation or certain latin1 chars.").
+         addOption("t", "troff",          false, "use troff format pages.").
+         //FIXME - should generate -T,--troff-device device
+         addOption("T", "troff-device",   true,  "use groff with selected device.").
+         addOption("Z", "ditroff",        false, "use groff with selected device.").
+         addOption("D", "default",        false, "reset all options to their default values.").
+         //FIXME - should generate -M,--manpath path
+         addOption("M", "manpath",        true,  "set search path for manual pages to 'path'.").
+         //FIXME - should generate -P,--pager pager
+         addOption("P", "pager",          true,  "use program 'pager' to display output.").
+         //FIXME - should generate -S,--sections list
+         addOption("S", "sections",       true,  "use colon separated section list.").
+         //FIXME - should generate -m,--systems system
+         addOption("m", "systems",        true,  "search for man pages from other unix system(s).").
+         //FIXME - should generate -L,--locale locale
+         addOption("L", "locale",         true,  "defaine the locale for this particular man search.").
+         //FIXME - should generate -p,--preprocessor string
+         addOption("p", "preprocessor",   true,  "string indicates which preprocessor to run.\n" +
+                                                 " e - [n]eqn  p - pic     t - tbl\n" +
+                                                 " g - grap    r - refer   v - vgrind").
+         addOption("V", "version",        false, "show version.").
+         addOption("h", "help",           false, "show this usage message.");
+
+      HelpFormatter hf = new HelpFormatter();
+      //hf.printHelp(cmdLine, opts);
+      hf.printHelp(60, cmdLine, null, opts, null);
+   }
+
+   static void bzip2Example()
+   {
+      System.out.println( "Coming soon" );
+   }
+
+   static void lsExample()
+   {
+      System.out.println( "Coming soon" );
+   }
+
+
+   // -------------------------------------------------------------- Attributes
+
+   // ------------------------------------------------------------ Constructors
+   
+   // ------------------------------------------------------------------ Public
+
+   // --------------------------------------------------------------- Protected
+
+   // ------------------------------------------------------- Package protected   
+   
+   // ----------------------------------------------------------------- Private
+   
+   // ----------------------------------------------------------- Inner classes
+
+}
diff --git a/installer/test/java/org/apache/commons/cli/HelpFormatterTest.java b/installer/test/java/org/apache/commons/cli/HelpFormatterTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/HelpFormatterTest.java
@@ -0,0 +1,82 @@
+package org.apache.commons.cli;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+import junit.framework.TestCase;
+
+public class HelpFormatterTest extends TestCase {
+
+    private Options _options;
+
+    protected void setUp() {
+        _options = new Options();
+        Option aOption = new Option("a", "Aa", false, "option A");
+        Option bOption = new Option("b", "Bb", false, "option B");
+        OptionGroup group1 = new OptionGroup();
+        group1.addOption(aOption);
+        group1.addOption(bOption);
+        _options.addOptionGroup(group1);
+    }
+
+    /**
+     * the setUp above used to print [-a | -b] [-a] [-b]
+     */
+    public void testOptionGroupDuplication() {
+        String help = unifyNewLines(getFormattedHelp());
+        String expectedHelp = unifyNewLines(new String("usage: syntax [-a | -b]\n-a,--Aa option A\n-b,--Bb option B\n"));
+        assertEquals("expected usage to be '" + expectedHelp + "' instead of '" + help + "'",
+                     expectedHelp,
+                     help);
+    }
+
+    /**
+     * Options following an option group used to be non blank separated: [-b | -a][-o] instead of
+     * [-b | -a] [-o]
+     */
+    public void testOptionGroupSubsequentOptions() {
+        _options.addOption(new Option("o", "Option O"));
+        String help = getFormattedHelp();
+        assertTrue(help.indexOf("][") < 0);
+        assertTrue(help.indexOf("[-a | -b] [-o]") >= 0);
+    }
+
+    //
+    // private methods
+    //
+    private String getFormattedHelp() {
+        HelpFormatter formatter = new HelpFormatter();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintWriter pw = new PrintWriter(baos);
+        formatter.printHelp(pw, 60, "syntax", null, _options, 0, 1, null, true);
+        pw.flush();
+        String usage = baos.toString();
+        return usage;
+    }
+
+    /**
+     * replace the windows specific \r\n line endings with java like \n line endings
+     * 
+     * @param in
+     *            The string to be transformed
+     * @return The string with unified line endings
+     */
+    private String unifyNewLines(String in) {
+        char[] inChars = in.toCharArray();
+        StringBuilder b = new StringBuilder(inChars.length);
+        for (int i = 0; i < inChars.length; i++) {
+            char current = inChars[i];
+            if (current == '\r') {
+                if (i < inChars.length) {
+                    char next = inChars[i + 1];
+                    if (next == '\n') {
+                        i++;
+                        current = next;
+                    }
+                }
+            }
+            b.append(current);
+        }
+        return b.toString();
+    }
+}
diff --git a/installer/test/java/org/apache/commons/cli/OptionBuilderTest.java b/installer/test/java/org/apache/commons/cli/OptionBuilderTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/OptionBuilderTest.java
@@ -0,0 +1,160 @@
+package org.apache.commons.cli;
+
+import java.math.BigDecimal;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import junit.textui.TestRunner;
+
+public class OptionBuilderTest extends TestCase {
+
+    public OptionBuilderTest( String name ) {
+        super( name );
+    }
+
+    public static Test suite() { 
+        return new TestSuite( OptionBuilderTest.class ); 
+    }
+
+    public static void main( String args[] ) { 
+        TestRunner.run( suite() );
+    }
+
+    public void testCompleteOption( ) {
+        OptionBuilder.withLongOpt( "simple option");
+        OptionBuilder.hasArg( );
+        OptionBuilder.isRequired( );
+        OptionBuilder.hasArgs( );
+        OptionBuilder.withType( new BigDecimal( "10" ) );
+        OptionBuilder.withDescription( "this is a simple option" );
+        Option simple = OptionBuilder.create( 's' );
+
+        assertEquals( "s", simple.getOpt() );
+        assertEquals( "simple option", simple.getLongOpt() );
+        assertEquals( "this is a simple option", simple.getDescription() );
+        assertEquals( simple.getType().getClass(), BigDecimal.class );
+        assertTrue( simple.hasArg() );
+        assertTrue( simple.isRequired() );
+        assertTrue( simple.hasArgs() );
+    }
+
+    public void testTwoCompleteOptions( ) {
+        OptionBuilder.withLongOpt( "simple option");
+        OptionBuilder.hasArg( );
+        OptionBuilder.isRequired( );
+        OptionBuilder.hasArgs( );
+        OptionBuilder.withType( new BigDecimal( "10" ) );
+        OptionBuilder.withDescription( "this is a simple option" );
+        Option simple = OptionBuilder.create( 's' );
+
+        assertEquals( "s", simple.getOpt() );
+        assertEquals( "simple option", simple.getLongOpt() );
+        assertEquals( "this is a simple option", simple.getDescription() );
+        assertEquals( simple.getType().getClass(), BigDecimal.class );
+        assertTrue( simple.hasArg() );
+        assertTrue( simple.isRequired() );
+        assertTrue( simple.hasArgs() );
+
+        OptionBuilder.withLongOpt( "dimple option");
+        OptionBuilder.hasArg( );
+        OptionBuilder.withDescription( "this is a dimple option" );
+        simple = OptionBuilder.create( 'd' );
+
+        assertEquals( "d", simple.getOpt() );
+        assertEquals( "dimple option", simple.getLongOpt() );
+        assertEquals( "this is a dimple option", simple.getDescription() );
+        assertNull( simple.getType() );
+        assertTrue( simple.hasArg() );
+        assertTrue( !simple.isRequired() );
+        assertTrue( !simple.hasArgs() );
+    }
+
+    public void testBaseOptionCharOpt() {
+        OptionBuilder.withDescription( "option description");
+        Option base = OptionBuilder.create( 'o' );
+
+        assertEquals( "o", base.getOpt() );
+        assertEquals( "option description", base.getDescription() );
+        assertTrue( !base.hasArg() );
+    }
+
+    public void testBaseOptionStringOpt() {
+        OptionBuilder.withDescription( "option description");
+        Option base = OptionBuilder.create( "o" );
+
+        assertEquals( "o", base.getOpt() );
+        assertEquals( "option description", base.getDescription() );
+        assertTrue( !base.hasArg() );
+    }
+
+    public void testSpecialOptChars() {
+
+        // '?'
+        try {
+            OptionBuilder.withDescription( "help options" );
+            Option opt = OptionBuilder.create( '?' );
+            assertEquals( "?", opt.getOpt() );
+        }
+        catch( IllegalArgumentException arg ) {
+            fail( "IllegalArgumentException caught" );
+        }
+
+        // '@'
+        try {
+            OptionBuilder.withDescription( "read from stdin" );
+            Option opt = OptionBuilder.create( '@' );
+            assertEquals( "@", opt.getOpt() );
+        }
+        catch( IllegalArgumentException arg ) {
+            fail( "IllegalArgumentException caught" );
+        }
+    }
+
+    public void testOptionArgNumbers() {
+        OptionBuilder.withDescription( "option description" );
+        OptionBuilder.hasArgs( 2 );
+        Option opt = OptionBuilder.create( 'o' );
+        assertEquals( 2, opt.getArgs() );
+    }
+
+    public void testIllegalOptions() {
+        // bad single character option
+        try {
+            OptionBuilder.withDescription( "option description" );
+            OptionBuilder.create( '"' );
+            fail( "IllegalArgumentException not caught" );
+        }
+        catch( IllegalArgumentException exp ) {
+            // success
+        }
+
+        // bad character in option string
+        try {
+            OptionBuilder.create( "opt`" );
+            fail( "IllegalArgumentException not caught" );
+        }
+        catch( IllegalArgumentException exp ) {
+            // success
+        }
+
+        // null option
+        try {
+            OptionBuilder.create( null );
+            fail( "IllegalArgumentException not caught" );
+        }
+        catch( IllegalArgumentException exp ) {
+            // success
+        }
+
+        // valid option 
+        try {
+            OptionBuilder.create( "opt" );
+            // success
+        }
+        catch( IllegalArgumentException exp ) {
+            fail( "IllegalArgumentException caught" );
+        }
+    }
+}
\ No newline at end of file
diff --git a/installer/test/java/org/apache/commons/cli/OptionGroupSortTest.java b/installer/test/java/org/apache/commons/cli/OptionGroupSortTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/OptionGroupSortTest.java
@@ -0,0 +1,43 @@
+package org.apache.commons.cli;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+public class OptionGroupSortTest extends TestCase {
+
+    private OptionGroup _optionGroup;
+
+    protected void setUp() {
+        _optionGroup = new OptionGroup();
+        _optionGroup.addOption(new Option("f", "first", false, "first"));
+        _optionGroup.addOption(new Option("s", "second", false, "second"));
+        _optionGroup.addOption(new Option("t", "third", false, "third"));
+    }
+
+    public void testSortNames() {
+        Collection names = _optionGroup.getNames();
+        Iterator namesIterator = names.iterator();
+        assertTrue(namesIterator.hasNext());
+        assertEquals("-f", (String) namesIterator.next());
+        assertTrue(namesIterator.hasNext());
+        assertEquals("-s", (String) namesIterator.next());
+        assertTrue(namesIterator.hasNext());
+        assertEquals("-t", (String) namesIterator.next());
+        assertFalse(namesIterator.hasNext());
+    }
+
+    public void testSortOptions() {
+        Collection options = _optionGroup.getOptions();
+        Iterator optionIterator = options.iterator();
+        assertTrue(optionIterator.hasNext());
+        assertEquals("first", ((Option) optionIterator.next()).getLongOpt());
+        assertTrue(optionIterator.hasNext());
+        assertEquals("second", ((Option) optionIterator.next()).getLongOpt());
+        assertTrue(optionIterator.hasNext());
+        assertEquals("third", ((Option) optionIterator.next()).getLongOpt());
+        assertFalse(optionIterator.hasNext());
+    }
+
+}
diff --git a/installer/test/java/org/apache/commons/cli/OptionGroupTest.java b/installer/test/java/org/apache/commons/cli/OptionGroupTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/OptionGroupTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: OptionGroupTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 3134 $
+ */
+public class OptionGroupTest extends TestCase
+{
+
+    private Options _options = null;
+    private CommandLineParser parser = new PosixParser();
+
+
+    public static Test suite() 
+    { 
+        return new TestSuite ( OptionGroupTest.class ); 
+    }
+
+    public OptionGroupTest( String name )
+    {
+        super( name );
+    }
+
+    public void setUp()
+    {
+        Option file = new Option( "f", "file", false, "file to process" );
+        Option dir = new Option( "d", "directory", false, "directory to process" );
+        OptionGroup group = new OptionGroup();
+        group.addOption( file );
+        group.addOption( dir );
+        _options = new Options().addOptionGroup( group );
+
+        Option section = new Option( "s", "section", false, "section to process" );
+        Option chapter = new Option( "c", "chapter", false, "chapter to process" );
+        OptionGroup group2 = new OptionGroup();
+        group2.addOption( section );
+        group2.addOption( chapter );
+
+        _options.addOptionGroup( group2 );
+        _options.addOption( "r", "revision", false, "revision number" );
+    }
+
+    public void tearDown()
+    {
+    }
+
+    public void testSingleOptionFromGroup()
+    {
+        String[] args = new String[] { "-f" };
+
+        try
+        {
+            CommandLine cl = parser.parse( _options, args);
+
+            assertTrue( "Confirm -r is NOT set", !cl.hasOption("r") );
+            assertTrue( "Confirm -f is set", cl.hasOption("f") );
+            assertTrue( "Confirm -d is NOT set", !cl.hasOption("d") );
+            assertTrue( "Confirm -s is NOT set", !cl.hasOption("s") );
+            assertTrue( "Confirm -c is NOT set", !cl.hasOption("c") );
+            assertTrue( "Confirm no extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testSingleOption()
+    {
+        String[] args = new String[] { "-r" };
+
+        try
+        {
+            CommandLine cl = parser.parse( _options, args);
+
+            assertTrue( "Confirm -r is set", cl.hasOption("r") );
+            assertTrue( "Confirm -f is NOT set", !cl.hasOption("f") );
+            assertTrue( "Confirm -d is NOT set", !cl.hasOption("d") );
+            assertTrue( "Confirm -s is NOT set", !cl.hasOption("s") );
+            assertTrue( "Confirm -c is NOT set", !cl.hasOption("c") );
+            assertTrue( "Confirm no extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testTwoValidOptions()
+    {
+        String[] args = new String[] { "-r", "-f" };
+
+        try
+        {
+            CommandLine cl = parser.parse( _options, args);
+
+            assertTrue( "Confirm -r is set", cl.hasOption("r") );
+            assertTrue( "Confirm -f is set", cl.hasOption("f") );
+            assertTrue( "Confirm -d is NOT set", !cl.hasOption("d") );
+            assertTrue( "Confirm -s is NOT set", !cl.hasOption("s") );
+            assertTrue( "Confirm -c is NOT set", !cl.hasOption("c") );
+            assertTrue( "Confirm no extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testSingleLongOption()
+    {
+        String[] args = new String[] { "--file" };
+
+        try
+        {
+            CommandLine cl = parser.parse( _options, args);
+
+            assertTrue( "Confirm -r is NOT set", !cl.hasOption("r") );
+            assertTrue( "Confirm -f is set", cl.hasOption("f") );
+            assertTrue( "Confirm -d is NOT set", !cl.hasOption("d") );
+            assertTrue( "Confirm -s is NOT set", !cl.hasOption("s") );
+            assertTrue( "Confirm -c is NOT set", !cl.hasOption("c") );
+            assertTrue( "Confirm no extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testTwoValidLongOptions()
+    {
+        String[] args = new String[] { "--revision", "--file" };
+
+        try
+        {
+            CommandLine cl = parser.parse( _options, args);
+
+            assertTrue( "Confirm -r is set", cl.hasOption("r") );
+            assertTrue( "Confirm -f is set", cl.hasOption("f") );
+            assertTrue( "Confirm -d is NOT set", !cl.hasOption("d") );
+            assertTrue( "Confirm -s is NOT set", !cl.hasOption("s") );
+            assertTrue( "Confirm -c is NOT set", !cl.hasOption("c") );
+            assertTrue( "Confirm no extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testNoOptionsExtraArgs()
+    {
+        String[] args = new String[] { "arg1", "arg2" };
+
+        try
+        {
+            CommandLine cl = parser.parse( _options, args);
+
+            assertTrue( "Confirm -r is NOT set", !cl.hasOption("r") );
+            assertTrue( "Confirm -f is NOT set", !cl.hasOption("f") );
+            assertTrue( "Confirm -d is NOT set", !cl.hasOption("d") );
+            assertTrue( "Confirm -s is NOT set", !cl.hasOption("s") );
+            assertTrue( "Confirm -c is NOT set", !cl.hasOption("c") );
+            assertTrue( "Confirm TWO extra args", cl.getArgList().size() == 2);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testTwoOptionsFromGroup()
+    {
+        String[] args = new String[] { "-f", "-d" };
+
+        CommandLine cl = null;
+        try
+        {
+            cl = parser.parse( _options, args);
+            fail( "two arguments from group not allowed" );
+        }
+        catch (ParseException e)
+        {
+            if( !( e instanceof AlreadySelectedException ) )
+            {
+                fail( "incorrect exception caught:" + e.getMessage() + " in " + cl );
+            }
+        }
+    }
+
+    public void testTwoLongOptionsFromGroup()
+    {
+        String[] args = new String[] { "--file", "--directory" };
+
+        CommandLine cl = null;
+        try
+        {
+            cl = parser.parse( _options, args);
+            fail( "two arguments from group not allowed" );
+        }
+        catch (ParseException e)
+        {
+            if( !( e instanceof AlreadySelectedException ) )
+            {
+                fail( "incorrect exception caught:" + e.getMessage() + " in " + cl );
+            }
+        }
+    }
+
+    public void testTwoOptionsFromDifferentGroup()
+    {
+        String[] args = new String[] { "-f", "-s" };
+
+        try
+        {
+            CommandLine cl = parser.parse( _options, args);
+            assertTrue( "Confirm -r is NOT set", !cl.hasOption("r") );
+            assertTrue( "Confirm -f is set", cl.hasOption("f") );
+            assertTrue( "Confirm -d is NOT set", !cl.hasOption("d") );
+            assertTrue( "Confirm -s is set", cl.hasOption("s") );
+            assertTrue( "Confirm -c is NOT set", !cl.hasOption("c") );
+            assertTrue( "Confirm NO extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+
+}
diff --git a/installer/test/java/org/apache/commons/cli/OptionsTest.java b/installer/test/java/org/apache/commons/cli/OptionsTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/OptionsTest.java
@@ -0,0 +1,28 @@
+package org.apache.commons.cli;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+
+public class OptionsTest extends TestCase {
+
+    private Options _options;
+
+    public void testSortAsAdded() {
+        _options = new Options();
+        _options.setSortAsAdded(true);
+        _options.addOption("f", "first", false, "first");
+        _options.addOption("s", "second", false, "second");
+        _options.addOption("t", "third", false, "third");
+        Collection optionCollection = _options.getOptions();
+        Iterator optionIterator = optionCollection.iterator();
+        assertTrue(optionIterator.hasNext());
+        assertEquals("first", ((Option) optionIterator.next()).getLongOpt());
+        assertTrue(optionIterator.hasNext());
+        assertEquals("second", ((Option) optionIterator.next()).getLongOpt());
+        assertTrue(optionIterator.hasNext());
+        assertEquals("third", ((Option) optionIterator.next()).getLongOpt());
+        assertFalse(optionIterator.hasNext());
+    }
+}
diff --git a/installer/test/java/org/apache/commons/cli/ParseRequiredTest.java b/installer/test/java/org/apache/commons/cli/ParseRequiredTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/ParseRequiredTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: ParseRequiredTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 3134 $
+ */
+public class ParseRequiredTest extends TestCase
+{
+
+    private Options _options = null;
+    private CommandLineParser parser = new PosixParser();
+
+    public static Test suite() { 
+        return new TestSuite(ParseRequiredTest.class); 
+    }
+
+    public ParseRequiredTest(String name)
+    {
+        super(name);
+    }
+
+    public void setUp()
+    {
+        OptionBuilder.withLongOpt( "bfile" );
+        OptionBuilder.hasArg();
+        OptionBuilder.isRequired();
+        OptionBuilder.withDescription( "set the value of [b]" );
+        Option opt2 = OptionBuilder.create( 'b' );
+        _options = new Options()
+            .addOption("a",
+                       "enable-a",
+                       false,
+                       "turn [a] on or off")
+            .addOption( opt2 );
+    }
+
+    public void tearDown()
+    {
+
+    }
+
+    public void testWithRequiredOption()
+    {
+        String[] args = new String[] {  "-b", "file" };
+
+        try
+        {
+            CommandLine cl = parser.parse(_options,args);
+            
+            assertTrue( "Confirm -a is NOT set", !cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("file") );
+            assertTrue( "Confirm NO of extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testOptionAndRequiredOption()
+    {
+        String[] args = new String[] {  "-a", "-b", "file" };
+
+        try
+        {
+            CommandLine cl = parser.parse(_options,args);
+
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("file") );
+            assertTrue( "Confirm NO of extra args", cl.getArgList().size() == 0);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testMissingRequiredOption()
+    {
+        String[] args = new String[] { "-a" };
+
+        CommandLine cl = null;
+        try
+        {
+            cl = parser.parse(_options,args);
+            fail( "exception should have been thrown" );
+        }
+        catch (ParseException e)
+        {
+            if( !( e instanceof MissingOptionException ) )
+            {
+                fail( "expected to catch MissingOptionException in " + cl );
+            }
+        }
+    }
+
+}
diff --git a/installer/test/java/org/apache/commons/cli/ParseTest.java b/installer/test/java/org/apache/commons/cli/ParseTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/ParseTest.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: ParseTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class ParseTest extends TestCase
+{
+
+    private Options _options = null;
+    private CommandLineParser _parser = null;
+
+    public static Test suite() { 
+        return new TestSuite(ParseTest.class); 
+    }
+
+    public ParseTest(String name)
+    {
+        super(name);
+    }
+
+    public void setUp()
+    {
+        _options = new Options()
+            .addOption("a",
+                       "enable-a",
+                       false,
+                       "turn [a] on or off")
+            .addOption("b",
+                       "bfile",
+                       true,
+                       "set the value of [b]")
+            .addOption("c",
+                       "copt",
+                       false,
+                       "turn [c] on or off");
+
+        _parser = new PosixParser();
+    }
+
+    public void tearDown()
+    {
+
+    }
+
+    public void testSimpleShort()
+    {
+        String[] args = new String[] { "-a",
+                                       "-b", "toast",
+                                       "foo", "bar" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+            
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm size of extra args", cl.getArgList().size() == 2);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testSimpleLong()
+    {
+        String[] args = new String[] { "--enable-a",
+                                       "--bfile", "toast",
+                                       "foo", "bar" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+            
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm size of extra args", cl.getArgList().size() == 2);
+        } 
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testComplexShort()
+    {
+        String[] args = new String[] { "-acbtoast",
+                                       "foo", "bar" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+            
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm size of extra args", cl.getArgList().size() == 2);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testExtraOption()
+    {
+        String[] args = new String[] { "-adbtoast",
+                                       "foo", "bar" };
+
+        boolean caught = false;
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+            
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm size of extra args", cl.getArgList().size() == 3);
+        }
+        catch (UnrecognizedOptionException e)
+        {
+            caught = true;
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+        assertTrue( "Confirm UnrecognizedOptionException caught", caught );
+    }
+
+    public void testMissingArg()
+    {
+
+        String[] args = new String[] { "-acb" };
+
+        boolean caught = false;
+
+        CommandLine cl = null;
+        try
+        {
+            cl = _parser.parse(_options, args);
+        }
+        catch (MissingArgumentException e)
+        {
+            caught = true;
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+
+        assertTrue( "Confirm MissingArgumentException caught " + cl, caught );
+    }
+
+    public void testStop()
+    {
+        String[] args = new String[] { "-c",
+                                       "foober",
+                                       "-btoast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args, true);
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm  2 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 2);
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testMultiple()
+    {
+        String[] args = new String[] { "-c",
+                                       "foobar",
+                                       "-btoast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args, true);
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm  2 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 2);
+
+            cl = _parser.parse(_options, cl.getArgs() );
+
+            assertTrue( "Confirm -c is not set", ! cl.hasOption("c") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm  1 extra arg: " + cl.getArgList().size(), cl.getArgList().size() == 1);
+            assertTrue( "Confirm  value of extra arg: " + cl.getArgList().get(0), cl.getArgList().get(0).equals("foobar") );
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testMultipleWithLong()
+    {
+        String[] args = new String[] { "--copt",
+                                       "foobar",
+                                       "--bfile", "toast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options,args,
+                                            true);
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm  3 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 3);
+
+            cl = _parser.parse(_options, cl.getArgs() );
+
+            assertTrue( "Confirm -c is not set", ! cl.hasOption("c") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("toast") );
+            assertTrue( "Confirm  1 extra arg: " + cl.getArgList().size(), cl.getArgList().size() == 1);
+            assertTrue( "Confirm  value of extra arg: " + cl.getArgList().get(0), cl.getArgList().get(0).equals("foobar") );
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testDoubleDash()
+    {
+        String[] args = new String[] { "--copt",
+                                       "--",
+                                       "-b", "toast" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+
+            assertTrue( "Confirm -c is set", cl.hasOption("c") );
+            assertTrue( "Confirm -b is not set", ! cl.hasOption("b") );
+            assertTrue( "Confirm 2 extra args: " + cl.getArgList().size(), cl.getArgList().size() == 2);
+
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+    }
+
+    public void testSingleDash()
+    {
+        String[] args = new String[] { "--copt",
+                                       "-b", "-",
+                                       "-a",
+                                       "-" };
+
+        try
+        {
+            CommandLine cl = _parser.parse(_options, args);
+
+            assertTrue( "Confirm -a is set", cl.hasOption("a") );
+            assertTrue( "Confirm -b is set", cl.hasOption("b") );
+            assertTrue( "Confirm arg of -b", cl.getOptionValue("b").equals("-") );
+            assertTrue( "Confirm 1 extra arg: " + cl.getArgList().size(), cl.getArgList().size() == 1);
+            assertTrue( "Confirm value of extra arg: " + cl.getArgList().get(0), cl.getArgList().get(0).equals("-") );
+        }
+        catch (ParseException e)
+        {
+            fail( e.toString() );
+        }
+        
+    }
+}
diff --git a/installer/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java b/installer/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/PatternOptionBuilderTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: PatternOptionBuilderTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+package org.apache.commons.cli;
+
+import java.math.BigDecimal;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/** 
+ * Test case for the PatternOptionBuilder class 
+ *
+ * @author Henri Yandell
+ **/
+public class PatternOptionBuilderTest
+extends TestCase
+{
+    public static void main( String[] args )
+   {
+      String[] testName = { PatternOptionBuilderTest.class.getName() };
+      junit.textui.TestRunner.main(testName);
+   }
+
+   public static TestSuite suite()
+   {
+      return new TestSuite(PatternOptionBuilderTest.class);
+   }
+
+   public PatternOptionBuilderTest( String s )
+   {
+      super( s );
+   }
+
+   public void testSimplePattern()
+   {
+       try {
+           Options options = PatternOptionBuilder.parsePattern("a:b at cde>f+n%t/");
+           String[] args = new String[] { "-c", "-a", "foo", "-b", "java.util.Vector", "-e", "build.xml", "-f", "java.util.Calendar", "-n", "4.5", "-t", "http://jakarta.apache.org/" };
+      
+           CommandLineParser parser = new PosixParser();
+           CommandLine line = parser.parse(options,args);
+
+           // tests the char methods of CommandLine that delegate to
+           // the String methods
+           assertEquals("flag a", "foo", line.getOptionValue("a"));
+           assertEquals("flag a", "foo", line.getOptionValue('a'));
+           assertEquals("string flag a", "foo", line.getOptionObject("a"));
+           assertEquals("string flag a", "foo", line.getOptionObject('a'));
+           assertEquals("object flag b", new java.util.Vector(), line.getOptionObject("b"));
+           assertEquals("object flag b", new java.util.Vector(), line.getOptionObject('b'));
+           assertEquals("boolean true flag c", true, line.hasOption("c"));
+           assertEquals("boolean true flag c", true, line.hasOption('c'));
+           assertEquals("boolean false flag d", false, line.hasOption("d"));
+           assertEquals("boolean false flag d", false, line.hasOption('d'));
+           assertEquals("file flag e", new java.io.File("build.xml"), line.getOptionObject("e"));
+           assertEquals("file flag e", new java.io.File("build.xml"), line.getOptionObject('e'));
+           assertEquals("class flag f", java.util.Calendar.class, line.getOptionObject("f"));
+           assertEquals("class flag f", java.util.Calendar.class, line.getOptionObject('f'));
+           assertEquals("number flag n", new BigDecimal("4.5"), line.getOptionObject("n"));
+           assertEquals("number flag n", new BigDecimal("4.5"), line.getOptionObject('n'));
+           assertEquals("url flag t", new java.net.URL("http://jakarta.apache.org/"), line.getOptionObject("t"));
+           assertEquals("url flag t", new java.net.URL("http://jakarta.apache.org/"), line.getOptionObject('t'));
+           /// DATES NOT SUPPORTED YET.
+           //      assertEquals("number flag t", new java.util.Date(1023400137276L), line.getOptionObject('z'));
+           //     input is:  "Thu Jun 06 17:48:57 EDT 2002"
+       }
+       catch( ParseException exp ) {
+           fail( exp.getMessage() );
+       }
+       catch( java.net.MalformedURLException exp ) {
+           fail( exp.getMessage() );
+       }
+   }
+
+}
diff --git a/installer/test/java/org/apache/commons/cli/PosixParserTest.java b/installer/test/java/org/apache/commons/cli/PosixParserTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/PosixParserTest.java
@@ -0,0 +1,138 @@
+package org.apache.commons.cli;
+
+import junit.framework.TestCase;
+
+public class PosixParserTest extends TestCase {
+
+    private static final String TEST_SHORT = "t";
+    private static final String TEST_LONG = "test";
+    private static final String TEST_DESC = "test option";
+
+    private static final String TEST_SHORT_OPTION = "-t";
+    private static final String TEST_LONG_OPTION = "--test";
+    
+    private static final String ARGUMENT = "argument";
+
+    private Options _options;
+    private Parser _parser;
+
+    protected void setUp() {
+        _parser = new PosixParser();
+
+        _options = new Options();
+        Option testOption = new Option(TEST_SHORT, TEST_LONG, false, TEST_DESC);
+        _options.addOption(testOption);
+    }
+
+    /**
+     * test that an unknown single option and a double hyphen option (with or without argument) are treated the same
+     */
+    public void testFlattenStop() {
+        boolean stopAtNonOption = true; // means unallowed tokens should not be added
+        String[] args;
+        String[] expectedFlattened;
+
+        // unknown single dash option
+        args = new String[] { "-u" };
+        expectedFlattened = new String[0];
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "-u", TEST_SHORT_OPTION };
+        expectedFlattened = new String[] { TEST_SHORT_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+
+        // unknown double dash option
+        args = new String[] { "--unknown" };
+        expectedFlattened = new String[0];
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "--unknown", TEST_LONG_OPTION };
+        expectedFlattened = new String[] { TEST_LONG_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+
+        // unknown double dash option with argument after =
+        args = new String[] { "--unknown=" + ARGUMENT };
+        expectedFlattened = new String[0];
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "--unknown="+ARGUMENT, TEST_LONG_OPTION };
+        expectedFlattened = new String[] { TEST_LONG_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+
+        // unknown double dash option with argument after ' '
+        args = new String[] { "--unknown", ARGUMENT };
+        expectedFlattened = new String[] { ARGUMENT };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "--unknown", ARGUMENT, TEST_LONG_OPTION };
+        expectedFlattened = new String[] { ARGUMENT, TEST_LONG_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+    }
+
+    /**
+     * test that an unknown single option and a double hyphen option (with or without argument) are treated the same
+     */
+    public void testFlattenNoStop() {
+        boolean stopAtNonOption = false; // means every token should be added
+        String[] args;
+        String[] expectedFlattened;
+
+        // unknown single dash option
+        args = new String[] { "-u" };
+        expectedFlattened = new String[] { "-u" };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "-u", TEST_SHORT_OPTION };
+        expectedFlattened = new String[] { "-u", TEST_SHORT_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+
+        // unknown double dash option
+        args = new String[] { "--unknown" };
+        expectedFlattened = new String[] { "--unknown" };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "--unknown", TEST_LONG_OPTION };
+        expectedFlattened = new String[] { "--unknown", TEST_LONG_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+
+        // unknown double dash option with argument after =
+        args = new String[] { "--unknown=" + ARGUMENT };
+        expectedFlattened = new String[] { "--unknown", ARGUMENT };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "--unknown="+ ARGUMENT, TEST_LONG_OPTION };
+        expectedFlattened = new String[] { "--unknown", ARGUMENT, TEST_LONG_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+
+        // unknown double dash option with argument after ' '
+        args = new String[] { "--unknown", ARGUMENT };
+        expectedFlattened = new String[] { "--unknown", ARGUMENT };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+        args = new String[] { "--unknown", ARGUMENT, TEST_LONG_OPTION };
+        expectedFlattened = new String[] { "--unknown", ARGUMENT, TEST_LONG_OPTION };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+    }
+
+    /**
+     * test that a misspelled long option (-test instead of --test) is not interpreted as -t est
+     */
+    public void testMisspelledLongOption() {
+        boolean stopAtNonOption = false; // means every token should be added
+        String[] args;
+        String[] expectedFlattened;
+
+        // unknown single dash long option
+        String singleDashLongOption = "-" + TEST_LONG; 
+        args = new String[] { singleDashLongOption };
+        expectedFlattened = new String[] { singleDashLongOption };
+        assertEquals(expectedFlattened, _parser.flatten(_options, args, stopAtNonOption));
+    }
+    
+    //
+    // private stuff
+    //
+
+    /**
+     * Assert that the content of the specified object arrays is equal
+     */
+    private void assertEquals(Object[] correct, Object[] tested) {
+        assertEquals("different array lengths:", correct.length, tested.length);
+        for (int i = 0; i < correct.length; i++) {
+            assertEquals("position " + i + " of array differs", correct[i], tested[i]);
+        }
+    }
+
+}
diff --git a/installer/test/java/org/apache/commons/cli/TestHelpFormatter.java b/installer/test/java/org/apache/commons/cli/TestHelpFormatter.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/TestHelpFormatter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: TestHelpFormatter.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+package org.apache.commons.cli;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/** 
+ * Test case for the HelpFormatter class 
+ *
+ * @author Slawek Zachcial
+ * @author John Keyes ( john at integralsource.com )
+ **/
+public class TestHelpFormatter extends TestCase
+{
+   public static void main( String[] args )
+   {
+      String[] testName = { TestHelpFormatter.class.getName() };
+      junit.textui.TestRunner.main(testName);
+   }
+
+   public static TestSuite suite()
+   {
+      return new TestSuite(TestHelpFormatter.class);
+   }
+
+   public TestHelpFormatter( String s )
+   {
+      super( s );
+   }
+
+   public void testFindWrapPos()
+      throws Exception
+   {
+      HelpFormatter hf = new HelpFormatter();
+
+      String text = "This is a test.";
+      //text width should be max 8; the wrap postition is 7
+      assertEquals("wrap position", 7, hf.findWrapPos(text, 8, 0));
+      //starting from 8 must give -1 - the wrap pos is after end
+      assertEquals("wrap position 2", -1, hf.findWrapPos(text, 8, 8));
+      //if there is no a good position before width to make a wrapping look for the next one
+      text = "aaaa aa";
+      assertEquals("wrap position 3", 4, hf.findWrapPos(text, 3, 0));
+   }
+
+   public void testPrintWrapped()
+      throws Exception
+   {
+      StringBuffer sb = new StringBuffer();
+      HelpFormatter hf = new HelpFormatter();
+
+      String text = "This is a test.";
+      String expected;
+
+      expected = "This is a" + hf.defaultNewLine + "test.";
+      hf.renderWrappedText(sb, 12, 0, text);
+      assertEquals("single line text", expected, sb.toString());
+
+      sb.setLength(0);
+      expected = "This is a" + hf.defaultNewLine + "    test.";
+      hf.renderWrappedText(sb, 12, 4, text);
+      assertEquals("single line padded text", expected, sb.toString());
+
+      text =
+         "aaaa aaaa aaaa" + hf.defaultNewLine +
+         "aaaaaa" + hf.defaultNewLine +
+         "aaaaa";
+
+      expected = text;
+      sb.setLength(0);
+      hf.renderWrappedText(sb, 16, 0, text);
+      assertEquals("multi line text", expected, sb.toString());
+
+      expected =
+         "aaaa aaaa aaaa" + hf.defaultNewLine +
+         "    aaaaaa" + hf.defaultNewLine +
+         "    aaaaa";
+      sb.setLength(0);
+      hf.renderWrappedText(sb, 16, 4, text);
+      assertEquals("multi-line padded text", expected, sb.toString());
+   }
+
+   public void testPrintOptions()
+   throws Exception
+   {
+       StringBuffer sb = new StringBuffer();
+       HelpFormatter hf = new HelpFormatter();
+       final int leftPad = 1;
+       final int descPad = 3;
+       final String lpad = hf.createPadding(leftPad);
+       final String dpad = hf.createPadding(descPad);
+       Options options = null;
+       String expected = null;
+
+       options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
+       expected = lpad + "-a" + dpad + "aaaa aaaa aaaa aaaa aaaa";
+       hf.renderOptions(sb, 60, options, leftPad, descPad);
+       assertEquals("simple non-wrapped option", expected, sb.toString());
+
+       int nextLineTabStop = leftPad+descPad+"-a".length();
+       expected =
+           lpad + "-a" + dpad + "aaaa aaaa aaaa" + hf.defaultNewLine +
+           hf.createPadding(nextLineTabStop) + "aaaa aaaa";
+       sb.setLength(0);
+       hf.renderOptions(sb, nextLineTabStop+17, options, leftPad, descPad);
+       assertEquals("simple wrapped option", expected, sb.toString());
+
+
+       options = new Options().addOption("a", "aaa", false, "dddd dddd dddd dddd");
+       expected = lpad + "-a,--aaa" + dpad + "dddd dddd dddd dddd";
+       sb.setLength(0);
+       hf.renderOptions(sb, 60, options, leftPad, descPad);
+       assertEquals("long non-wrapped option", expected, sb.toString());
+
+       nextLineTabStop = leftPad+descPad+"-a,--aaa".length();
+       expected =
+           lpad + "-a,--aaa" + dpad + "dddd dddd" + hf.defaultNewLine +
+           hf.createPadding(nextLineTabStop) + "dddd dddd";
+       sb.setLength(0);
+       hf.renderOptions(sb, 25, options, leftPad, descPad);
+       assertEquals("long wrapped option", expected, sb.toString());
+
+       options = new Options().
+           addOption("a", "aaa", false, "dddd dddd dddd dddd").
+           addOption("b", false, "feeee eeee eeee eeee");
+       expected =
+           lpad + "-a,--aaa" + dpad + "dddd dddd" + hf.defaultNewLine +
+           hf.createPadding(nextLineTabStop) + "dddd dddd" + hf.defaultNewLine +
+           lpad + "-b      " + dpad + "feeee eeee" + hf.defaultNewLine +
+           hf.createPadding(nextLineTabStop) + "eeee eeee";
+       sb.setLength(0);
+       hf.renderOptions(sb, 25, options, leftPad, descPad);
+       assertEquals("multiple wrapped options", expected, sb.toString());
+   }
+
+   public void testAutomaticUsage()
+   throws Exception
+   {
+       HelpFormatter hf = new HelpFormatter();
+       Options options = null;
+       String expected = "usage: app [-a]";
+       ByteArrayOutputStream out = new ByteArrayOutputStream( );
+       PrintWriter pw = new PrintWriter( out );
+
+       options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa");
+       hf.printUsage( pw, 60, "app", options );
+       pw.flush();
+       assertEquals("simple auto usage", expected, out.toString().trim());
+       out.reset();
+
+       expected = "usage: app [-b] [-a]";
+       options = new Options().addOption("a", false, "aaaa aaaa aaaa aaaa aaaa")
+       .addOption("b", false, "bbb" );
+       hf.printUsage( pw, 60, "app", options );
+       pw.flush();
+       assertEquals("simple auto usage", expected, out.toString().trim());
+       out.reset();
+   }
+}
diff --git a/installer/test/java/org/apache/commons/cli/ValueTest.java b/installer/test/java/org/apache/commons/cli/ValueTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/ValueTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: ValueTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class ValueTest extends TestCase
+{
+
+    public static Test suite() { 
+        return new TestSuite(ValueTest.class); 
+    }
+
+    private CommandLine _cl = null;
+    private Options opts = new Options();
+
+    public ValueTest(String name)
+    {
+        super(name);
+    }
+
+    public void setUp()
+    {
+        opts.addOption("a",
+                       false,
+                       "toggle -a");
+
+        opts.addOption("b",
+                       true,
+                       "set -b");
+
+        opts.addOption("c",
+                       "c",
+                       false,
+                       "toggle -c");
+
+        opts.addOption("d",
+                       "d",
+                       true,
+                       "set -d");
+
+        OptionBuilder.hasOptionalArg();
+        opts.addOption( OptionBuilder.create( 'e') );
+
+        OptionBuilder.hasOptionalArg();
+        OptionBuilder.withLongOpt( "fish" );
+        opts.addOption( OptionBuilder.create( ) );
+
+        OptionBuilder.hasOptionalArgs();
+        OptionBuilder.withLongOpt( "gravy" );
+        opts.addOption( OptionBuilder.create( ) );
+
+        OptionBuilder.hasOptionalArgs( 2 );
+        OptionBuilder.withLongOpt( "hide" );
+        opts.addOption( OptionBuilder.create( ) );
+
+        OptionBuilder.hasOptionalArgs( 2 );
+        opts.addOption( OptionBuilder.create( 'i' ) );
+
+        OptionBuilder.hasOptionalArgs( );
+        opts.addOption( OptionBuilder.create( 'j' ) );
+
+        String[] args = new String[] { "-a",
+            "-b", "foo",
+            "--c",
+            "--d", "bar" 
+        };
+
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            _cl = parser.parse(opts,args);
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void tearDown()
+    {
+
+    }
+
+    public void testShortNoArg()
+    {
+        assertTrue( _cl.hasOption("a") );
+        assertNull( _cl.getOptionValue("a") );
+    }
+
+    public void testShortWithArg()
+    {
+        assertTrue( _cl.hasOption("b") );
+        assertNotNull( _cl.getOptionValue("b") );
+        assertEquals( _cl.getOptionValue("b"), "foo");
+    }
+
+    public void testLongNoArg()
+    {
+        assertTrue( _cl.hasOption("c") );
+        assertNull( _cl.getOptionValue("c") );
+    }
+
+    public void testLongWithArg()
+    {
+        assertTrue( _cl.hasOption("d") );
+        assertNotNull( _cl.getOptionValue("d") );
+        assertEquals( _cl.getOptionValue("d"), "bar");
+    }
+
+    public void testShortOptionalArgNoValue()
+    {
+        String[] args = new String[] { "-e"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("e") );
+            assertNull( cmd.getOptionValue("e") );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void testShortOptionalArgValue()
+    {
+        String[] args = new String[] { "-e", "everything"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("e") );
+            assertEquals( "everything", cmd.getOptionValue("e") );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void testLongOptionalNoValue()
+    {
+        String[] args = new String[] { "--fish"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("fish") );
+            assertNull( cmd.getOptionValue("fish") );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void testLongOptionalArgValue()
+    {
+        String[] args = new String[] { "--fish", "face"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("fish") );
+            assertEquals( "face", cmd.getOptionValue("fish") );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void testShortOptionalArgValues()
+    {
+        String[] args = new String[] { "-j", "ink", "idea"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("j") );
+            assertEquals( "ink", cmd.getOptionValue("j") );
+            assertEquals( "ink", cmd.getOptionValues("j")[0] );
+            assertEquals( "idea", cmd.getOptionValues("j")[1] );
+            assertEquals( cmd.getArgs().length, 0 );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void testLongOptionalArgValues()
+    {
+        String[] args = new String[] { "--gravy", "gold", "garden"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("gravy") );
+            assertEquals( "gold", cmd.getOptionValue("gravy") );
+            assertEquals( "gold", cmd.getOptionValues("gravy")[0] );
+            assertEquals( "garden", cmd.getOptionValues("gravy")[1] );
+            assertEquals( cmd.getArgs().length, 0 );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void testShortOptionalNArgValues()
+    {
+        String[] args = new String[] { "-i", "ink", "idea", "isotope", "ice"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("i") );
+            assertEquals( "ink", cmd.getOptionValue("i") );
+            assertEquals( "ink", cmd.getOptionValues("i")[0] );
+            assertEquals( "idea", cmd.getOptionValues("i")[1] );
+            assertEquals( cmd.getArgs().length, 2 );
+            assertEquals( "isotope", cmd.getArgs()[0] );
+            assertEquals( "ice", cmd.getArgs()[1] );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void testLongOptionalNArgValues()
+    {
+        String[] args = new String[] { "--hide", "house", "hair", "head"
+        };
+        try
+        {
+            CommandLineParser parser = new PosixParser();
+            CommandLine cmd = parser.parse(opts,args);
+            assertTrue( cmd.hasOption("hide") );
+            assertEquals( "house", cmd.getOptionValue("hide") );
+            assertEquals( "house", cmd.getOptionValues("hide")[0] );
+            assertEquals( "hair", cmd.getOptionValues("hide")[1] );
+            assertEquals( cmd.getArgs().length, 1 );
+            assertEquals( "head", cmd.getArgs()[0] );
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+}
diff --git a/installer/test/java/org/apache/commons/cli/ValuesTest.java b/installer/test/java/org/apache/commons/cli/ValuesTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/apache/commons/cli/ValuesTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE file.
+ * 
+ * $Id: ValuesTest.java 3134 2007-03-02 07:20:08Z otmarhumbel $
+ */
+
+package org.apache.commons.cli;
+
+import java.util.Arrays;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class ValuesTest extends TestCase
+{
+    /** CommandLine instance */
+    private CommandLine _cmdline = null;
+    private Option _option = null;
+
+    public static Test suite() { 
+        return new TestSuite( ValuesTest.class );
+    }
+
+    public ValuesTest( String name )
+    {
+        super( name );
+    }
+
+    public void setUp()
+    {
+        Options opts = new Options();
+
+        opts.addOption("a",
+                       false,
+                       "toggle -a");
+
+        opts.addOption("b",
+                       true,
+                       "set -b");
+
+        opts.addOption("c",
+                       "c",
+                       false,
+                       "toggle -c");
+
+        opts.addOption("d",
+                       "d",
+                       true,
+                       "set -d");
+        
+        OptionBuilder.withLongOpt( "e" );
+        OptionBuilder.hasArgs();
+        OptionBuilder.withDescription( "set -e ");
+        opts.addOption( OptionBuilder.create( 'e' ) );
+
+        opts.addOption("f",
+                       "f",
+                       false,
+                       "jk");
+        
+        OptionBuilder.withLongOpt( "g" );
+        OptionBuilder.hasArgs( 2 );
+        OptionBuilder.withDescription( "set -g");
+        opts.addOption( OptionBuilder.create( 'g' ) );
+
+        OptionBuilder.withLongOpt( "h" );
+        OptionBuilder.hasArgs( 2 );
+        OptionBuilder.withDescription( "set -h");
+        opts.addOption( OptionBuilder.create( 'h' ) );
+
+        OptionBuilder.withLongOpt( "i" );
+        OptionBuilder.withDescription( "set -i");
+        opts.addOption( OptionBuilder.create( 'i' ) );
+
+        OptionBuilder.withLongOpt( "j" );
+        OptionBuilder.hasArgs( );
+        OptionBuilder.withDescription( "set -j");
+        OptionBuilder.withValueSeparator( '=' );
+        opts.addOption( OptionBuilder.create( 'j' ) );
+
+        OptionBuilder.withLongOpt( "k" );
+        OptionBuilder.hasArgs( );
+        OptionBuilder.withDescription( "set -k");
+        OptionBuilder.withValueSeparator( '=' );
+        opts.addOption( OptionBuilder.create( 'k' ) );
+
+        OptionBuilder.withLongOpt( "m" );
+        OptionBuilder.hasArgs( );
+        OptionBuilder.withDescription( "set -m");
+        OptionBuilder.withValueSeparator( );
+        _option = OptionBuilder.create( 'm' );
+
+        opts.addOption( _option );
+        
+        String[] args = new String[] { "-a",
+                                       "-b", "foo",
+                                       "--c",
+                                       "--d", "bar",
+                                       "-e", "one", "two",
+                                       "-f",
+                                       "arg1", "arg2",
+                                       "-g", "val1", "val2" , "arg3",
+                                       "-h", "val1", "-i",
+                                       "-h", "val2",
+                                       "-jkey=value",
+                                       "-j", "key=value",
+                                       "-kkey1=value1", 
+                                       "-kkey2=value2",
+                                       "-mkey=value"};
+
+        CommandLineParser parser = new PosixParser();
+
+        try
+        {
+            _cmdline = parser.parse(opts,args);
+        }
+        catch (ParseException e)
+        {
+            fail("Cannot setUp() CommandLine: " + e.toString());
+        }
+    }
+
+    public void tearDown()
+    {
+
+    }
+
+    public void testShortArgs()
+    {
+        assertTrue( _cmdline.hasOption("a") );
+        assertTrue( _cmdline.hasOption("c") );
+
+        assertNull( _cmdline.getOptionValues("a") );
+        assertNull( _cmdline.getOptionValues("c") );
+    }
+
+    public void testShortArgsWithValue()
+    {
+        assertTrue( _cmdline.hasOption("b") );
+        assertTrue( _cmdline.getOptionValue("b").equals("foo"));
+        assertTrue( _cmdline.getOptionValues("b").length == 1);
+
+        assertTrue( _cmdline.hasOption("d") );
+        assertTrue( _cmdline.getOptionValue("d").equals("bar"));
+        assertTrue( _cmdline.getOptionValues("d").length == 1);
+    }
+
+    public void testMultipleArgValues()
+    {
+        _cmdline.getOptionValues("e");
+        String[] values = new String[] { "one", "two" };
+        assertTrue( _cmdline.hasOption("e") );
+        assertTrue( _cmdline.getOptionValues("e").length == 2);
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues("e") ) );
+    }
+
+    public void testTwoArgValues()
+    {
+        _cmdline.getOptionValues("g");
+        String[] values = new String[] { "val1", "val2" };
+        assertTrue( _cmdline.hasOption("g") );
+        assertTrue( _cmdline.getOptionValues("g").length == 2);
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues("g") ) );
+    }
+
+    public void testComplexValues()
+    {
+        _cmdline.getOptionValues("h");
+        String[] values = new String[] { "val1", "val2" };
+        assertTrue( _cmdline.hasOption("i") );
+        assertTrue( _cmdline.hasOption("h") );
+        assertTrue( _cmdline.getOptionValues("h").length == 2);
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues("h") ) );
+    }
+
+    public void testExtraArgs()
+    {
+        String[] args = new String[] { "arg1", "arg2", "arg3" };
+        assertTrue( _cmdline.getArgs().length == 3 );         
+        assertTrue( Arrays.equals( args, _cmdline.getArgs() ) );
+    }
+
+    public void testCharSeparator()
+    {
+        // tests the char methods of CommandLine that delegate to
+        // the String methods
+        String[] values = new String[] { "key", "value", "key", "value" };
+        assertTrue( _cmdline.hasOption( "j" ) );
+        assertTrue( _cmdline.hasOption( 'j' ) );
+        assertTrue( _cmdline.getOptionValues( "j" ).length == 4);
+        assertTrue( _cmdline.getOptionValues( 'j' ).length == 4);
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues( "j" ) ) );
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues( 'j' ) ) );
+
+        values = new String[] { "key1", "value1", "key2", "value2" };
+        assertTrue( _cmdline.hasOption( "k" ) );
+        assertTrue( _cmdline.hasOption( 'k' ) );
+        assertTrue( _cmdline.getOptionValues( "k" ).length == 4 );
+        assertTrue( _cmdline.getOptionValues( 'k' ).length == 4 );
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues( "k" ) ) );
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues( 'k' ) ) );
+
+        values = new String[] { "key", "value" };
+        assertTrue( _cmdline.hasOption( "m" ) );
+        assertTrue( _cmdline.hasOption( 'm' ) );
+        assertTrue( _cmdline.getOptionValues( "m" ).length == 2);
+        assertTrue( _cmdline.getOptionValues( 'm' ).length == 2);
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues( "m" ) ) );
+        assertTrue( Arrays.equals( values, _cmdline.getOptionValues( 'm' ) ) );
+    }
+
+    /**
+     * jkeyes - commented out this test as the new architecture
+     * breaks this type of functionality.  I have left the test 
+     * here in case I get a brainwave on how to resolve this.
+     */
+    /*
+    public void testGetValue()
+    {
+        // the 'm' option
+        assertTrue( _option.getValues().length == 2 );
+        assertEquals( _option.getValue(), "key" );
+        assertEquals( _option.getValue( 0 ), "key" );
+        assertEquals( _option.getValue( 1 ), "value" );
+
+        try {
+            assertEquals( _option.getValue( 2 ), "key" );
+            fail( "IndexOutOfBounds not caught" );
+        }
+        catch( IndexOutOfBoundsException exp ) {
+            
+        }
+
+        try {
+            assertEquals( _option.getValue( -1 ), "key" );
+            fail( "IndexOutOfBounds not caught" );
+        }
+        catch( IndexOutOfBoundsException exp ) {
+
+        }
+    }
+    */
+}
diff --git a/installer/test/java/org/python/util/install/ChildProcessExample.java b/installer/test/java/org/python/util/install/ChildProcessExample.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/ChildProcessExample.java
@@ -0,0 +1,32 @@
+
+package org.python.util.install;
+
+/**
+ * An example child process that generates some output.
+ */
+public class ChildProcessExample {
+
+  public ChildProcessExample() {
+    System.out.println("[ChildProcessExample] is now here.");
+  }
+
+  public static void main(String args[]) {
+    int i = 0;
+    new ChildProcessExample();
+    for (i = 0; i < 10; i++) {
+      System.out.println("[ChildProcessExample] printing to stdout " + i);
+      // occasionally print to stderr, too
+      if (i % 3 == 0) {
+        System.err.println("[ChildProcessExample] printing to stderr " + i);
+      }
+      try {
+        Thread.sleep(500);
+      } catch (InterruptedException ie) {
+        ie.printStackTrace();
+      }
+    }
+    System.out.println("[ChildProcessExample] Exiting");
+    System.exit(0);
+  }
+
+}
\ No newline at end of file
diff --git a/installer/test/java/org/python/util/install/ChildProcessTest.java b/installer/test/java/org/python/util/install/ChildProcessTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/ChildProcessTest.java
@@ -0,0 +1,80 @@
+package org.python.util.install;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+import junit.framework.TestCase;
+
+public class ChildProcessTest extends TestCase {
+
+    private final static String CLASS_NAME = "org.python.util.install.ChildProcessExample";
+
+    /**
+     * test a default child process
+     */
+    public void testDefault() {
+        ChildProcess childProcess = new ChildProcess();
+        String command[] = buildJavaCommand(CLASS_NAME);
+        childProcess.setCommand(command);
+        childProcess.setDebug(true);
+        int exitValue = childProcess.run();
+        assertEquals("Expected child process to end normally instead of " + exitValue, 0, exitValue);
+    }
+
+    /**
+     * test the child process with a timeout
+     */
+    public void testTimeout() {
+        ChildProcess childProcess = new ChildProcess();
+        String command[] = buildJavaCommand(CLASS_NAME);
+        childProcess.setCommand(command);
+        childProcess.setDebug(true);
+        childProcess.setTimeout(2000); // timeout to 2 seconds
+        int exitValue = childProcess.run();
+        assertEquals("Expected child process to be destroyed instead of " + exitValue,
+                     ChildProcess.DESTROYED_AFTER_TIMEOUT,
+                     exitValue);
+    }
+
+    /**
+     * test silent mode
+     */
+    public void testSilent() throws IOException {
+        ChildProcess childProcess = new ChildProcess();
+        String command[] = new String[] {"lwiklsl", "-siwK"};
+        childProcess.setCommand(command);
+        childProcess.setDebug(false);
+        childProcess.setSilent(true);
+        ByteArrayOutputStream redirectedErr = new ByteArrayOutputStream();
+        ByteArrayOutputStream redirectedOut = new ByteArrayOutputStream();
+        int exitValue = 0;
+        PrintStream oldErr = System.err;
+        PrintStream oldOut = System.out;
+        try {
+            System.setErr(new PrintStream(redirectedErr));
+            System.setOut(new PrintStream(redirectedOut));
+            exitValue = childProcess.run();
+        } finally {
+            System.setErr(oldErr);
+            System.setOut(oldOut);
+        }
+        assertTrue(0 != exitValue);
+        redirectedErr.flush();
+        redirectedOut.flush();
+        assertEquals(0, redirectedErr.size());
+        assertEquals(0, redirectedOut.size());
+    }
+
+    //
+    // private methods
+    //
+    private String[] buildJavaCommand(String classAndArguments) {
+        String quote = "";
+        if (System.getProperty("os.name", "unknown").toLowerCase().indexOf("windows") >= 0) {
+            quote = "\"";
+        }
+        String classpath = System.getProperty("java.class.path");
+        return new String[] {"java",  "-classpath",  quote.concat(classpath).concat(quote), classAndArguments};
+    }
+}
\ No newline at end of file
diff --git a/installer/test/java/org/python/util/install/ChmodTest_Standalone.java b/installer/test/java/org/python/util/install/ChmodTest_Standalone.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/ChmodTest_Standalone.java
@@ -0,0 +1,57 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Helper class to test of 'chmod' on different platforms
+ */
+public class ChmodTest_Standalone {
+
+    private static String _mode = "755"; // default mode
+
+    public static void main(String[] args) {
+        // get mode from first argument, if present
+        if (args.length > 0) {
+            _mode = args[0];
+        }
+
+        // create an empty test file in the current directory
+        String curdir = System.getProperty("user.dir");
+        File testFile = new File(curdir, "chmod.test");
+        String path = testFile.getAbsolutePath();
+        if (!testFile.exists()) {
+            try {
+                if (!testFile.createNewFile()) {
+                    System.err.println(getPrefix() + "unable to create file " + path);
+                    System.exit(1);
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+                System.exit(1);
+            }
+        }
+
+        // apply the chmod command on the test file
+        if (!testFile.exists()) {
+            System.err.println(getPrefix() + "unable to create file " + path);
+            System.exit(1);
+        } else {
+            String command[] = new String[] {"chmod", _mode, path};
+            ChildProcess childProcess = new ChildProcess(command, 3000);
+            childProcess.setDebug(true);
+            int exitValue = childProcess.run();
+            if (exitValue != 0) {
+                System.err.println(getPrefix() + "error during chmod");
+            } else {
+                System.out.println(getPrefix() + "chmod command executed on " + path);
+            }
+            System.exit(exitValue);
+        }
+    }
+
+    private static String getPrefix() {
+        return "[ChmodTest_Standalone] ";
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/FileHelperTest.java b/installer/test/java/org/python/util/install/FileHelperTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/FileHelperTest.java
@@ -0,0 +1,271 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+
+import junit.framework.TestCase;
+import junit.runner.TestSuiteLoader;
+
+import org.python.util.install.driver.Autotest;
+
+public class FileHelperTest extends TestCase {
+
+    private static final String JYTHON_TEST_TEMPLATE = "jython_test.template";
+
+    private static final String LOGO_GIF = "logo.gif";
+
+    private static final String JYTHON_SMALL_C_PNG = "jython_small_c.png";
+
+    public void testCreateTempDirectory_WasFile() throws IOException {
+        File file = File.createTempFile("some_prefix", "");
+        assertTrue(file.exists());
+        assertTrue(file.isFile());
+        assertTrue(FileHelper.createTempDirectory(file));
+        assertTrue(file.exists());
+        assertTrue(file.isDirectory());
+    }
+
+    public void testCreateTempDirectory_AlreadyPresent() throws IOException {
+        File dir = new File(System.getProperty("user.dir"));
+        assertTrue(dir.exists());
+        assertTrue(dir.isDirectory());
+        assertTrue(FileHelper.createTempDirectory(dir));
+        assertTrue(dir.exists());
+        assertTrue(dir.isDirectory());
+    }
+
+    public void testCreateTempDirectory() throws IOException {
+        File dir = new File(System.getProperty("user.dir"));
+        assertTrue(dir.exists());
+        assertTrue(dir.isDirectory());
+        File tmpDir = new File(dir, "tmp");
+        assertFalse(tmpDir.exists());
+        try {
+            assertTrue(FileHelper.createTempDirectory(tmpDir));
+            assertTrue(tmpDir.exists());
+            assertTrue(dir.isDirectory());
+        } finally {
+            if (tmpDir.exists()) {
+                assertTrue(tmpDir.delete());
+            }
+        }
+    }
+
+    public void testCreateTempDirectory_Failure() throws Exception {
+        File dir = new File(System.getProperty("user.dir"));
+        assertTrue(dir.exists());
+        assertTrue(dir.isDirectory());
+        File tmpFile = new File(dir, "tmpFailure");
+        assertFalse(tmpFile.exists());
+        try {
+            tmpFile.createNewFile();
+            assertTrue(tmpFile.exists());
+            assertTrue(tmpFile.isFile());
+            Lock lock = null;
+            try {
+                lock = new Lock(tmpFile);
+                boolean created = FileHelper.createTempDirectory(tmpFile);
+                if (Installation.isWindows()) {
+                    // locking currently only effective on windows
+                    assertFalse(created);
+                } else {
+                    // change if there is a locking mechanism
+                    assertTrue(created);
+                }
+            } finally {
+                if (lock != null) {
+                    lock.release();
+                }
+            }
+        } finally {
+            if (tmpFile.exists()) {
+                assertTrue(tmpFile.delete());
+            }
+        }
+    }
+
+    public void testRmdir() throws IOException {
+        File dir = new File(System.getProperty("java.io.tmpdir"), "StartScriptGeneratorTest");
+        if (!dir.exists()) {
+            assertTrue(dir.mkdirs());
+        }
+        File bin = new File(dir, "bin");
+        if (!bin.exists()) {
+            assertTrue(bin.mkdirs());
+        }
+        File jython = new File(bin, "jython");
+        if (!jython.exists()) {
+            assertTrue(jython.createNewFile());
+        }
+        File jython_bat = new File(bin, "jython.bat");
+        if (!jython_bat.exists()) {
+            assertTrue(jython_bat.createNewFile());
+        }
+        assertTrue(FileHelper.rmdir(dir));
+    }
+
+    public void testRmdir_Failure() throws Exception {
+        File dir = new File(System.getProperty("java.io.tmpdir"), "StartScriptGeneratorTest");
+        if (!dir.exists()) {
+            assertTrue(dir.mkdirs());
+        }
+        File bin = new File(dir, "bin");
+        if (!bin.exists()) {
+            assertTrue(bin.mkdirs());
+        }
+        File jython = new File(bin, "jython");
+        if (!jython.exists()) {
+            assertTrue(jython.createNewFile());
+        }
+        File jython_bat = new File(bin, "jython.bat");
+        if (!jython_bat.exists()) {
+            assertTrue(jython_bat.createNewFile());
+        }
+        Lock lock = null;
+        try {
+            lock = new Lock(jython_bat);
+            boolean removed = FileHelper.rmdir(dir);
+            if (Installation.isWindows()) {
+                // locking currently only effective on windows
+                assertFalse(removed);
+            } else {
+                // change if there is a locking mechanism
+                assertTrue(removed);
+            }
+        } finally {
+            if (lock != null) {
+                lock.release();
+            }
+            assertTrue(FileHelper.rmdir(dir));
+        }
+    }
+
+    public void testReadAll() throws Exception {
+        File file = File.createTempFile("testReadAll", "");
+        final String contents = new String("line1 \n line2 \n");
+        FileHelper.write(file, contents);
+        String readContents = FileHelper.readAll(file);
+        assertEquals(contents, readContents);
+    }
+
+    public void testReadAll_InputStream() throws Exception {
+        URL url = FileHelper.getRelativeURL(Autotest.class, JYTHON_TEST_TEMPLATE);
+        assertNotNull(url);
+        URI uri = new URI(url.toString());
+        File file = new File(uri);
+        assertNotNull(file);
+        assertTrue(file.exists());
+        String expectedContents = FileHelper.readAll(file);
+        InputStream is = FileHelper.getRelativeURLAsStream(Autotest.class, JYTHON_TEST_TEMPLATE);
+        assertNotNull(is);
+        String contents = FileHelper.readAll(is);
+        assertEquals(expectedContents, contents);
+        // now from a .jar
+        is = FileHelper.getRelativeURLAsStream(TestSuiteLoader.class, LOGO_GIF);
+        assertNotNull(is);
+        contents = FileHelper.readAll(is);
+        assertNotNull(contents);
+        assertEquals(964, contents.length());
+        assertTrue(contents.startsWith("GIF89a&"));
+    }
+
+    public void testReadAll_NonExisting() {
+        String readContents = null;
+        try {
+            readContents = FileHelper.readAll(new File("_non_existing"));
+            fail("FileNotFoundException expected");
+        } catch (IOException e) {
+            assertNull(readContents);
+        }
+    }
+
+    public void testGetRelativeURL() {
+        URL url = FileHelper.getRelativeURL(Installation.class, JYTHON_SMALL_C_PNG);
+        assertNotNull(url);
+        assertTrue(url.getPath().endsWith("org/python/util/install/".concat(JYTHON_SMALL_C_PNG)));
+        // now from a .jar
+        url = FileHelper.getRelativeURL(TestSuiteLoader.class, LOGO_GIF);
+        assertNotNull(url);
+        assertTrue(url.getPath().endsWith("!/junit/runner/".concat(LOGO_GIF)));
+    }
+
+    public void testGetRelativeURLAsStream() throws IOException {
+        InputStream is = FileHelper.getRelativeURLAsStream(Installation.class, JYTHON_SMALL_C_PNG);
+        assertNotNull(is);
+        try {
+            assertTrue(is.read() >= 0);
+        } finally {
+            is.close();
+        }
+        // now from a .jar
+        is = FileHelper.getRelativeURLAsStream(TestSuiteLoader.class, LOGO_GIF);
+        assertNotNull(is);
+        try {
+            assertTrue(is.read() >= 0);
+        } finally {
+            is.close();
+        }
+    }
+
+    public void testWrite() throws IOException {
+        File file = new File("testWrite");
+        assertFalse(file.exists());
+        try {
+            final String contents = new String("line1 \n line2 \n");
+            FileHelper.write(file, contents);
+            assertTrue(file.exists());
+            String readContents = FileHelper.readAll(file);
+            assertEquals(contents, readContents);
+        } finally {
+            if (file.exists()) {
+                assertTrue(file.delete());
+            }
+        }
+    }
+
+    public void testWrite_Existing() throws IOException {
+        File file = File.createTempFile("testWrite", "");
+        assertTrue(file.exists());
+        final String firstContents = "first dummy contents";
+        FileHelper.write(file, firstContents);
+        assertEquals(firstContents, FileHelper.readAll(file));
+        final String realContents = new String("line1 \n line2 \n");
+        FileHelper.write(file, realContents);
+        assertEquals(realContents, FileHelper.readAll(file));
+    }
+
+    /**
+     * A poor man's file lock (does work on windows only)
+     */
+    public static class Lock {
+
+        private final FileOutputStream _fos;
+
+        /**
+         * acquire a file lock
+         * 
+         * @param file
+         *            The file to be locked
+         * @throws FileNotFoundException
+         */
+        public Lock(File file) throws FileNotFoundException {
+            _fos = new FileOutputStream(file);
+        }
+
+        /**
+         * release the file lock
+         * 
+         * @throws IOException
+         */
+        public void release() throws IOException {
+            if (_fos != null) {
+                _fos.close();
+            }
+        }
+    }
+}
diff --git a/installer/test/java/org/python/util/install/FrameInstallerTest.java b/installer/test/java/org/python/util/install/FrameInstallerTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/FrameInstallerTest.java
@@ -0,0 +1,88 @@
+package org.python.util.install;
+
+import org.python.util.install.Installation.JavaVersionInfo;
+
+import junit.framework.TestCase;
+
+public class FrameInstallerTest extends TestCase {
+
+    public void testInitDefaultJava() {
+        FrameInstaller.initDefaultJava();
+        JavaVersionInfo vInfo = FrameInstaller.getJavaVersionInfo();
+        assertNotNull(vInfo);
+        String version = vInfo.getVersion();
+        assertNotNull(version);
+        assertTrue(version.length() > 0);
+        String specificationVersion = vInfo.getSpecificationVersion();
+        assertNotNull(specificationVersion);
+        assertTrue(specificationVersion.length() > 0);
+        String vendor = vInfo.getVendor();
+        assertNotNull(vendor);
+        assertTrue(vendor.length() > 0);
+    }
+    
+    public void testJavaVersionInfo() {
+        String version = "1;2;3";
+        String vendor = "jython [macrosystems]";
+        String specificationVersion = "@spec 1,4";
+
+        JavaVersionInfo vInfo = new JavaVersionInfo();
+        vInfo.setVersion(version);
+        vInfo.setVendor(vendor);
+        vInfo.setSpecificationVersion(specificationVersion);
+
+        FrameInstaller.setJavaVersionInfo(vInfo);
+        JavaVersionInfo returnedInfo = FrameInstaller.getJavaVersionInfo();
+
+        assertNotNull(returnedInfo);
+        assertEquals(version, returnedInfo.getVersion());
+        assertEquals(vendor, returnedInfo.getVendor());
+        assertEquals(specificationVersion, returnedInfo.getSpecificationVersion());
+    }
+
+    public void testInstallationType() {
+        InstallationType installationType = new InstallationType();
+        installationType.addLibraryModules();
+        installationType.removeDemosAndExamples();
+        installationType.removeDocumentation();
+        installationType.addSources();
+
+        FrameInstaller.setInstallationType(installationType);
+        InstallationType returnedType = FrameInstaller.getInstallationType();
+
+        assertNotNull(returnedType);
+        assertTrue(returnedType.installLibraryModules());
+        assertFalse(returnedType.installDemosAndExamples());
+        assertFalse(returnedType.installDocumentation());
+        assertTrue(returnedType.installSources());
+    }
+
+    public void testStandalone() {
+        InstallationType installationType = new InstallationType();
+        installationType.setStandalone();
+        assertTrue(installationType.installLibraryModules());
+        assertFalse(installationType.installDemosAndExamples());
+        assertFalse(installationType.installDocumentation());
+        assertFalse(installationType.installSources());
+
+        FrameInstaller.setInstallationType(installationType);
+        InstallationType returnedType = FrameInstaller.getInstallationType();
+
+        assertNotNull(returnedType);
+        assertTrue(returnedType.isStandalone());
+        assertTrue(returnedType.installLibraryModules());
+        assertFalse(returnedType.installDemosAndExamples());
+        assertFalse(returnedType.installDocumentation());
+        assertFalse(returnedType.installSources());
+    }
+    
+    public void testSetGetJavaHomeHandler() {
+        assertNotNull(FrameInstaller.getJavaHomeHandler());
+        JavaHomeHandler handler1 = new JavaHomeHandler();
+        JavaHomeHandler handler2 = new JavaHomeHandler("some/dir");
+        FrameInstaller.setJavaHomeHandler(handler1);
+        assertEquals(handler1, FrameInstaller.getJavaHomeHandler());
+        FrameInstaller.setJavaHomeHandler(handler2);
+        assertEquals(handler2, FrameInstaller.getJavaHomeHandler());
+    }
+}
diff --git a/installer/test/java/org/python/util/install/InstallationTest.java b/installer/test/java/org/python/util/install/InstallationTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/InstallationTest.java
@@ -0,0 +1,107 @@
+package org.python.util.install;
+
+import java.io.File;
+
+import org.python.util.install.Installation.JavaVersionInfo;
+
+import junit.framework.TestCase;
+
+public class InstallationTest extends TestCase {
+
+    public void testGetExternalJavaVersion() {
+        JavaHomeHandler javaHomeHandler = new JavaHomeHandler();
+        JavaVersionInfo versionInfo = Installation.getExternalJavaVersion(javaHomeHandler);
+        assertEquals(Installation.NORMAL_RETURN, versionInfo.getErrorCode());
+        assertEquals("", versionInfo.getReason());
+        assertTrue(versionInfo.getVersion().length() > 0);
+        assertTrue(versionInfo.getSpecificationVersion().length() > 0);
+        assertTrue(versionInfo.getVersion().startsWith(versionInfo.getSpecificationVersion()));
+        assertNotNull(versionInfo.getVendor());
+        assertNotSame("", versionInfo.getVendor());
+    }
+
+    public void testGetExternalJavaVersionWithError() {
+        JavaHomeHandler javaHomeHandler = new JavaHomeHandler("non_existing/home");
+        JavaVersionInfo versionInfo = Installation.getExternalJavaVersion(javaHomeHandler);
+        assertEquals(Installation.ERROR_RETURN, versionInfo.getErrorCode());
+        String reason = versionInfo.getReason();
+        assertTrue(reason.indexOf("invalid") >= 0);
+    }
+
+    public void testGetExternalJavaVersionNoBinDirectory() {
+        File wrongHome = new File(System.getProperty("user.home"));
+        JavaHomeHandler javaHomeHandler = new JavaHomeHandler(wrongHome.getAbsolutePath());
+        JavaVersionInfo versionInfo = Installation.getExternalJavaVersion(javaHomeHandler);
+        assertEquals(Installation.ERROR_RETURN, versionInfo.getErrorCode());
+        String reason = versionInfo.getReason();
+        assertTrue(reason.indexOf("invalid") >= 0);
+    }
+
+    public void testGetExternalJavaVersionNoJavaInBinDirectory() {
+        File wrongHome = new File(System.getProperty("user.home"));
+        File binDir = new File(wrongHome, "bin");
+        assertFalse(binDir.exists());
+        try {
+            assertTrue(binDir.mkdirs());
+            JavaHomeHandler javaHomeHandler = new JavaHomeHandler(wrongHome.getAbsolutePath());
+            JavaVersionInfo versionInfo = Installation.getExternalJavaVersion(javaHomeHandler);
+            assertEquals(Installation.ERROR_RETURN, versionInfo.getErrorCode());
+            assertTrue(versionInfo.getReason().indexOf("invalid") >= 0);
+        } finally {
+            if (binDir.exists()) {
+                binDir.delete();
+            }
+        }
+    }
+
+    public void testIsValidJavaVersion() {
+        JavaVersionInfo javaVersionInfo = new JavaVersionInfo();
+
+        javaVersionInfo.setSpecificationVersion("1.1.9");
+        assertFalse(Installation.isValidJava(javaVersionInfo));
+        javaVersionInfo.setSpecificationVersion("1.2");
+        assertFalse(Installation.isValidJava(javaVersionInfo));
+        javaVersionInfo.setSpecificationVersion("1.3");
+        assertFalse(Installation.isValidJava(javaVersionInfo));
+        javaVersionInfo.setSpecificationVersion("1.4");
+        assertFalse(Installation.isValidJava(javaVersionInfo));
+        javaVersionInfo.setSpecificationVersion("1.5");
+        assertTrue(Installation.isValidJava(javaVersionInfo));
+        javaVersionInfo.setSpecificationVersion("1.6");
+        assertTrue(Installation.isValidJava(javaVersionInfo));
+        javaVersionInfo.setSpecificationVersion("1.7");
+        assertTrue(Installation.isValidJava(javaVersionInfo));
+    }
+
+    public void testGetJavaSpecificationVersion() {
+        String specificationVersion = "1.4.2";
+        assertEquals(14, Installation.getJavaSpecificationVersion(specificationVersion));
+        specificationVersion = "1.5.0";
+        assertEquals(15, Installation.getJavaSpecificationVersion(specificationVersion));
+        specificationVersion = "1.6.0";
+        assertEquals(16, Installation.getJavaSpecificationVersion(specificationVersion));
+    }
+
+    public void testIsGNUJava() {
+        assertFalse(Installation.isGNUJava());
+        String originalVmName = System.getProperty(Installation.JAVA_VM_NAME);
+        try {
+            // fake GNU java
+            System.setProperty(Installation.JAVA_VM_NAME, "GNU libgcj");
+            assertTrue(Installation.isGNUJava());
+        } finally {
+            System.setProperty(Installation.JAVA_VM_NAME, originalVmName);
+            assertFalse(Installation.isGNUJava());
+        }
+    }
+    
+    public void testGetDefaultJavaVersion() {
+        JavaVersionInfo info = Installation.getDefaultJavaVersion();
+        assertNotNull(info);
+        assertEquals(Installation.NORMAL_RETURN, info.getErrorCode());
+        String specVersion = info.getSpecificationVersion();
+        assertNotNull(specVersion);
+        assertTrue(specVersion.length() >= 3);
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/InstallationTypeTest.java b/installer/test/java/org/python/util/install/InstallationTypeTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/InstallationTypeTest.java
@@ -0,0 +1,119 @@
+package org.python.util.install;
+
+import junit.framework.TestCase;
+
+// test checkin
+public class InstallationTypeTest extends TestCase {
+
+    private InstallationType _type;
+
+    protected void setUp() {
+        _type = new InstallationType();
+    }
+
+    public void testConstruction() {
+        assertTrue(_type.isStandard());
+        assertFalse(_type.isMinimum());
+        assertFalse(_type.isAll());
+        assertFalse(_type.isStandalone());
+        assertTrue(_type.isPredefined());
+    }
+
+    public void testStandard() {
+        _type.setStandard();
+        assertTrue(_type.isStandard());
+        assertTrue(_type.installLibraryModules());
+        assertTrue(_type.installDemosAndExamples());
+        assertTrue(_type.installDocumentation());
+        assertFalse(_type.installSources());
+        assertFalse(_type.isStandalone());
+        assertTrue(_type.isPredefined());
+    }
+
+    public void testMinimum() {
+        assertFalse(_type.isMinimum());
+        _type.setMinimum();
+        assertTrue(_type.isMinimum());
+        assertFalse(_type.installLibraryModules());
+        assertFalse(_type.installDemosAndExamples());
+        assertFalse(_type.installDocumentation());
+        assertFalse(_type.installSources());
+        assertFalse(_type.isStandalone());
+        assertTrue(_type.isPredefined());
+    }
+
+    public void testAll() {
+        assertFalse(_type.isAll());
+        _type.setAll();
+        assertTrue(_type.isAll());
+        assertTrue(_type.installLibraryModules());
+        assertTrue(_type.installDemosAndExamples());
+        assertTrue(_type.installDocumentation());
+        assertTrue(_type.installSources());
+        assertFalse(_type.isStandalone());
+        assertTrue(_type.isPredefined());
+    }
+
+    public void testStandalone() {
+        assertFalse(_type.isStandalone());
+        _type.setStandalone();
+        assertTrue(_type.isStandalone());
+        assertFalse(_type.isMinimum());
+        assertFalse(_type.isStandard());
+        assertFalse(_type.isAll());
+        assertTrue(_type.isPredefined());
+
+        // sure to handle this as follows?
+        assertTrue(_type.installLibraryModules());
+        assertFalse(_type.installDemosAndExamples());
+        assertFalse(_type.installDocumentation());
+        assertFalse(_type.installSources());
+    }
+
+    public void testAddRemove() {
+        _type.removeDocumentation();
+        assertTrue(_type.installLibraryModules());
+        assertTrue(_type.installDemosAndExamples());
+        assertFalse(_type.installDocumentation());
+        assertFalse(_type.installSources());
+        assertFalse(_type.isMinimum());
+        assertFalse(_type.isStandard());
+        assertFalse(_type.isAll());
+        assertFalse(_type.isStandalone());
+        assertFalse(_type.isPredefined());
+
+        _type.removeDemosAndExamples();
+        assertTrue(_type.installLibraryModules());
+        assertFalse(_type.installDemosAndExamples());
+        assertFalse(_type.installDocumentation());
+        assertFalse(_type.installSources());
+        assertFalse(_type.isMinimum());
+        assertFalse(_type.isStandard());
+        assertFalse(_type.isAll());
+        assertFalse(_type.isStandalone());
+        assertFalse(_type.isPredefined());
+
+        _type.addSources();
+        assertTrue(_type.installLibraryModules());
+        assertFalse(_type.installDemosAndExamples());
+        assertFalse(_type.installDocumentation());
+        assertTrue(_type.installSources());
+        assertFalse(_type.isMinimum());
+        assertFalse(_type.isStandard());
+        assertFalse(_type.isAll());
+        assertFalse(_type.isStandalone());
+        assertFalse(_type.isPredefined());
+
+        _type.addDocumentation();
+        assertTrue(_type.installLibraryModules());
+        assertFalse(_type.installDemosAndExamples());
+        assertTrue(_type.installDocumentation());
+        assertTrue(_type.installSources());
+        assertFalse(_type.isMinimum());
+        assertFalse(_type.isStandard());
+        assertFalse(_type.isAll());
+        assertFalse(_type.isStandalone());
+        assertFalse(_type.isPredefined());
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/InstallerCommandLineTest.java b/installer/test/java/org/python/util/install/InstallerCommandLineTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/InstallerCommandLineTest.java
@@ -0,0 +1,637 @@
+package org.python.util.install;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+
+public class InstallerCommandLineTest extends TestCase {
+
+    public void testValidArguments() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[0];
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertFalse(commandLine.hasArguments());
+
+        args = new String[0];
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertFalse(commandLine.hasArguments());
+
+        args = new String[] { "-c" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "--directory", "c:/temp" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "--type", "all" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-t", "minimum" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-type", "standard" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-v" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "--verbose" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-A" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "--autotest" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[0];
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertFalse(commandLine.hasArguments());
+    }
+
+    public void testInvalidArguments() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[] { "--one" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "--one argOne" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "--one", "--two", "--three" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-o" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-type" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-type", "weird" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+        assertTrue(commandLine.hasArguments());
+
+        args = new String[] { "-" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+    }
+
+    public void testMissingArgument() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[] { "--directory" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "--type" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+    }
+
+    public void testUnknownArgument() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[] { "yeah" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "--silent", "yeah" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "--silent", "yeah", "yoyo" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "--type", "takatuka" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+    }
+
+    public void testOptionGroups() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[] { "-s", "-c" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "-s", "-A" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "-c", "-A" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "--silent", "--console" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "--silent", "--autotest" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "--console", "--autotest" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "-?", "-h" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+
+        args = new String[] { "-?", "--help" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+    }
+
+    public void testSilent() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[] { "-s" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args)); // expect required directory in silent mode
+
+        args = new String[] { "-s", "-d", "/tmp" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasSilentOption());
+    }
+
+    public void testConsole() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[] { "-c" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasConsoleOption());
+    }
+    
+    public void testGui() {
+        String[] args;
+        InstallerCommandLine commandLine;
+        
+        // normal gui startup without any arguments
+        assertTrue(Installation.isGuiAllowed());
+        args = new String[0];
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertFalse(commandLine.hasConsoleOption());
+        assertFalse(commandLine.hasSilentOption());
+    }
+    
+    /**
+     *  simulate startup on a headless system (auto-switch to console mode)
+     */
+    public void testHeadless() {
+        String[] args;
+        InstallerCommandLine commandLine;
+        boolean originalHeadless = Boolean.getBoolean(Installation.HEADLESS_PROPERTY_NAME);
+        try {
+            if (!originalHeadless) {
+                System.setProperty(Installation.HEADLESS_PROPERTY_NAME, "true");
+            }
+            assertFalse(Installation.isGuiAllowed());
+            
+            // without any arguments
+            args = new String[0];
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertTrue(commandLine.hasConsoleOption()); // auto switch
+            assertFalse(commandLine.hasSilentOption());
+            
+            // with one argument
+            args = new String[] {"-v"};
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertTrue(commandLine.hasVerboseOption());
+            assertTrue(commandLine.hasConsoleOption()); // auto switch
+            assertFalse(commandLine.hasSilentOption());
+            
+            // with more arguments
+            args = new String[] {"-v", "-t", "minimum" };
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertTrue(commandLine.hasVerboseOption());
+            assertTrue(commandLine.hasConsoleOption()); // auto switch
+            assertFalse(commandLine.hasSilentOption());
+            assertTrue(commandLine.hasTypeOption());
+            InstallationType type = commandLine.getInstallationType();
+            assertNotNull(type);
+            assertTrue(type.isMinimum());
+            
+            // silent should override!
+            args = new String[] {"-v", "-s", "-d", "some_dir"};
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertTrue(commandLine.hasVerboseOption());
+            assertFalse(commandLine.hasConsoleOption()); // no auto switch
+            assertTrue(commandLine.hasSilentOption());
+            assertTrue(commandLine.hasDirectoryOption());
+            File dir = commandLine.getTargetDirectory();
+            assertNotNull(dir);
+            assertEquals("some_dir", dir.getName());
+            
+            // -A (autotest) should override as well
+            args = new String[] {"-A"};
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertFalse(commandLine.hasVerboseOption());
+            assertFalse(commandLine.hasConsoleOption()); // no auto switch
+            assertFalse(commandLine.hasSilentOption());
+            
+            // console aready present should be no problem
+            args = new String[] {"-c", "-v"};
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertTrue(commandLine.hasVerboseOption());
+            assertTrue(commandLine.hasConsoleOption());
+            assertFalse(commandLine.hasSilentOption());
+        } finally {
+            if (!originalHeadless) {
+                System.setProperty(Installation.HEADLESS_PROPERTY_NAME, "false");
+                assertTrue(Installation.isGuiAllowed());
+            }
+        }
+    }
+
+    public void testGNUSwitchToConsole() {
+        String originalVmName = System.getProperty(Installation.JAVA_VM_NAME);
+        try {
+            // fake GNU java
+            System.setProperty(Installation.JAVA_VM_NAME, "GNU libgcj");
+            assertTrue(Installation.isGNUJava());
+            String[] args;
+            InstallerCommandLine commandLine;
+            // expect auto switch
+            args = new String[] {"-v"};
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertTrue(commandLine.hasVerboseOption());
+            assertTrue(commandLine.hasConsoleOption()); // auto switch
+            // expect no auto switch
+            args = new String[] {"-s", "-d", "some_dir"};
+            commandLine = new InstallerCommandLine();
+            assertTrue(commandLine.setArgs(args));
+            assertTrue(commandLine.hasSilentOption());
+            assertFalse(commandLine.hasVerboseOption());
+            assertFalse(commandLine.hasConsoleOption()); // no auto switch
+            assertTrue(commandLine.hasDirectoryOption());
+            File dir = commandLine.getTargetDirectory();
+            assertNotNull(dir);
+            assertEquals("some_dir", dir.getName());
+        } finally {
+            System.setProperty(Installation.JAVA_VM_NAME, originalVmName);
+            assertFalse(Installation.isGNUJava());
+        }
+    }
+
+
+    public void testDirectory() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[] { "-d", "dir" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasDirectoryOption());
+        assertNotNull(commandLine.getTargetDirectory());
+        assertEquals("dir", commandLine.getTargetDirectory().getName());
+
+        args = new String[] { "-s", "--directory", "dir" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasDirectoryOption());
+        assertNotNull(commandLine.getTargetDirectory());
+        assertEquals("dir", commandLine.getTargetDirectory().getName());
+    }
+
+    public void testType() {
+        String[] args;
+        InstallerCommandLine commandLine;
+        InstallationType type;
+
+        args = new String[] { "-t", "all" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasTypeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertTrue(type.isAll());
+
+        args = new String[] { "--type", "standard" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasTypeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertTrue(type.isStandard());
+
+        args = new String[] { "--type", "minimum" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasTypeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertTrue(type.isMinimum());
+
+        args = new String[] { "--type", "standalone" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasTypeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertTrue(type.isStandalone());
+
+        assertFalse(commandLine.getJavaHomeHandler().isDeviation());
+    }
+
+    public void testInclude() {
+        String[] args;
+        InstallerCommandLine commandLine;
+        InstallationType type;
+
+        args = new String[] { "-t", "minimum", "-i", "mod" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasIncludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertTrue(type.installLibraryModules());
+        assertFalse(type.installDemosAndExamples());
+        assertFalse(type.installDocumentation());
+        assertFalse(type.installSources());
+
+        args = new String[] { "-t", "minimum", "-i", "mod", "demo" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasIncludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertTrue(type.installLibraryModules());
+        assertTrue(type.installDemosAndExamples());
+        assertFalse(type.installDocumentation());
+        assertFalse(type.installSources());
+
+        args = new String[] { "-t", "minimum", "-i", "mod", "demo", "doc" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasIncludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertTrue(type.installLibraryModules());
+        assertTrue(type.installDemosAndExamples());
+        assertTrue(type.installDocumentation());
+        assertFalse(type.installSources());
+
+        args = new String[] { "-t", "minimum", "--include", "mod", "demo", "doc", "src" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasIncludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertTrue(type.installLibraryModules());
+        assertTrue(type.installDemosAndExamples());
+        assertTrue(type.installDocumentation());
+        assertTrue(type.installSources());
+
+        args = new String[] { "-i", "modulo" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+    }
+
+    public void testExclude() {
+        String[] args;
+        InstallerCommandLine commandLine;
+        InstallationType type;
+
+        args = new String[] { "-t", "all", "-e", "mod" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasExcludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertFalse(type.installLibraryModules());
+        assertTrue(type.installDemosAndExamples());
+        assertTrue(type.installDocumentation());
+        assertTrue(type.installSources());
+
+        args = new String[] { "-t", "all", "-e", "mod", "demo" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasExcludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertFalse(type.installLibraryModules());
+        assertFalse(type.installDemosAndExamples());
+        assertTrue(type.installDocumentation());
+        assertTrue(type.installSources());
+
+        args = new String[] { "-t", "all", "-e", "mod", "demo", "doc" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasExcludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertFalse(type.installLibraryModules());
+        assertFalse(type.installDemosAndExamples());
+        assertFalse(type.installDocumentation());
+        assertTrue(type.installSources());
+
+        args = new String[] { "-t", "all", "--exclude", "mod", "demo", "doc", "src" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasExcludeOption());
+        type = commandLine.getInstallationType();
+        assertNotNull(type);
+        assertFalse(type.isStandalone());
+        assertFalse(type.installLibraryModules());
+        assertFalse(type.installDemosAndExamples());
+        assertFalse(type.installDocumentation());
+        assertFalse(type.installSources());
+
+        args = new String[] { "--exclude", "sources" };
+        commandLine = new InstallerCommandLine();
+        assertFalse(commandLine.setArgs(args));
+    }
+
+    public void testJavaHomeHandler() {
+        String[] args;
+        InstallerCommandLine commandLine;
+        final String javaHomeName = "java/home/dir";
+
+        args = new String[] { "-j", javaHomeName };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasJavaHomeOption());
+        JavaHomeHandler javaHomeHandler = commandLine.getJavaHomeHandler();
+        assertNotNull(javaHomeHandler);
+        assertTrue(javaHomeHandler.toString().indexOf(javaHomeName) >= 0);
+        assertTrue(javaHomeHandler.isDeviation());
+
+        args = new String[] { "--jre", javaHomeName };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasJavaHomeOption());
+        javaHomeHandler = commandLine.getJavaHomeHandler();
+        assertNotNull(javaHomeHandler);
+        assertTrue(javaHomeHandler.toString().indexOf(javaHomeName) >= 0);
+        assertTrue(javaHomeHandler.isDeviation());
+
+        assertNull(commandLine.getTargetDirectory());
+    }
+
+    public void testExamples() {
+        String[] args;
+        InstallerCommandLine commandLine;
+        final String javaHomeName = "java/home/dir";
+
+        args = new String[] { "-c" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasConsoleOption());
+
+        args = new String[] { "-s", "-d", "dir" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasSilentOption());
+        assertTrue(commandLine.hasDirectoryOption());
+        assertNotNull(commandLine.getTargetDirectory());
+        assertEquals("dir", commandLine.getTargetDirectory().getName());
+
+        args = new String[] { "-s", "-d", "dir", "-t", "standard", "-j", javaHomeName, "-v" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasSilentOption());
+        assertTrue(commandLine.hasDirectoryOption());
+        assertNotNull(commandLine.getTargetDirectory());
+        assertEquals("dir", commandLine.getTargetDirectory().getName());
+        assertTrue(commandLine.hasTypeOption());
+        assertNotNull(commandLine.getInstallationType());
+        assertTrue(commandLine.getInstallationType().installDemosAndExamples());
+        assertTrue(commandLine.getInstallationType().installDocumentation());
+        assertTrue(commandLine.getInstallationType().installLibraryModules());
+        assertFalse(commandLine.getInstallationType().installSources());
+        assertTrue(commandLine.hasJavaHomeOption());
+        JavaHomeHandler javaHomeHandler = commandLine.getJavaHomeHandler();
+        assertTrue(javaHomeHandler.toString().indexOf(javaHomeName) >= 0);
+        assertTrue(javaHomeHandler.isDeviation());
+        assertTrue(commandLine.hasVerboseOption());
+
+        args = new String[] { "-s", "-d", "dir", "-t", "standard", "-e", "doc", "demo", "-i", "src", "-j", javaHomeName, "-v" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasSilentOption());
+        assertTrue(commandLine.hasDirectoryOption());
+        assertNotNull(commandLine.getTargetDirectory());
+        assertEquals("dir", commandLine.getTargetDirectory().getName());
+        assertTrue(commandLine.hasTypeOption());
+        assertNotNull(commandLine.getInstallationType());
+        assertFalse(commandLine.getInstallationType().installDemosAndExamples());
+        assertFalse(commandLine.getInstallationType().installDocumentation());
+        assertTrue(commandLine.getInstallationType().installLibraryModules());
+        assertTrue(commandLine.getInstallationType().installSources());
+        assertTrue(commandLine.hasJavaHomeOption());
+        javaHomeHandler = commandLine.getJavaHomeHandler();
+        assertTrue(javaHomeHandler.toString().indexOf(javaHomeName) >= 0);
+        assertTrue(javaHomeHandler.isDeviation());
+        assertTrue(commandLine.hasVerboseOption());
+    }
+
+    public void testHelp() {
+        String[] args;
+        InstallerCommandLine commandLine;
+
+        args = new String[0];
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertFalse(commandLine.hasHelpOption());
+
+        args = new String[] { "--help" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasHelpOption());
+
+        args = new String[] { "-h" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasHelpOption());
+
+        args = new String[] { "-?" };
+        commandLine = new InstallerCommandLine();
+        assertTrue(commandLine.setArgs(args));
+        assertTrue(commandLine.hasHelpOption());
+
+        // now print the help
+        commandLine.printHelp();
+    }
+
+    public void testHasVerboseOptionInArgs() {
+        String[] args = new String[0];
+        assertFalse(InstallerCommandLine.hasVerboseOptionInArgs(args));
+
+        args = new String[] {"a", "b", "c"};
+        assertFalse(InstallerCommandLine.hasVerboseOptionInArgs(args));
+ 
+        args = new String[] {"a", InstallerCommandLine.VERBOSE_SHORT, "c"};
+        assertFalse(InstallerCommandLine.hasVerboseOptionInArgs(args));
+        
+        args = new String[] {"a", "-" + InstallerCommandLine.VERBOSE_SHORT, "c"};
+        assertTrue(InstallerCommandLine.hasVerboseOptionInArgs(args));
+        
+        args = new String[] {"a", InstallerCommandLine.VERBOSE_LONG, "c"};
+        assertFalse(InstallerCommandLine.hasVerboseOptionInArgs(args));
+        
+        args = new String[] {"a", "-" + InstallerCommandLine.VERBOSE_LONG, "c"};
+        assertFalse(InstallerCommandLine.hasVerboseOptionInArgs(args));
+        
+        args = new String[] {"a", "--" + InstallerCommandLine.VERBOSE_LONG, "c"};
+        assertTrue(InstallerCommandLine.hasVerboseOptionInArgs(args));
+    }
+    
+}
diff --git a/installer/test/java/org/python/util/install/JavaHomeHandlerTest.java b/installer/test/java/org/python/util/install/JavaHomeHandlerTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/JavaHomeHandlerTest.java
@@ -0,0 +1,114 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+public class JavaHomeHandlerTest extends TestCase {
+
+    private static final String JAVA_HOME = JavaHomeHandler.JAVA_HOME;
+
+    private static final String JAVA = "java";
+
+    private static final String SOME_WEIRD_HOME = "some/weird/home";
+
+    private String _originalJavaHome;
+
+    @Override
+    protected void setUp() throws Exception {
+        JavaHomeHandler.reset();
+        _originalJavaHome = System.getProperty(JAVA_HOME);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        System.setProperty(JAVA_HOME, _originalJavaHome);
+    }
+
+    public void testGetExecutableName() throws IOException {
+        String executable = new JavaHomeHandler().getExecutableName();
+        assertNotNull(executable);
+        assertTrue(executable.length() > JAVA.length());
+        String homePath = createTempHome().getAbsolutePath();
+        executable = new JavaHomeHandler(homePath).getExecutableName();
+        assertTrue(executable.length() > JAVA.length());
+        assertTrue(executable.indexOf(homePath) >= 0);
+        System.setProperty(JAVA_HOME, homePath);
+        executable = new JavaHomeHandler().getExecutableName();
+        assertTrue(executable.length() > JAVA.length());
+        assertTrue(executable.indexOf(homePath) >= 0);
+    }
+
+    public void testGetExecutableName_NonExisting() {
+        String executable = new JavaHomeHandler(SOME_WEIRD_HOME).getExecutableName();
+        assertEquals(JAVA, executable); // fallback
+        System.setProperty(JAVA_HOME, SOME_WEIRD_HOME);
+        executable = new JavaHomeHandler().getExecutableName();
+        assertEquals(JAVA, executable); // fallback
+    }
+
+    public void testCreateJavaHomeHandler() throws IOException {
+        JavaHomeHandler handler = new JavaHomeHandler();
+        assertNotNull(handler);
+        System.setProperty(JAVA_HOME, SOME_WEIRD_HOME);
+        handler = new JavaHomeHandler();
+        assertNotNull(handler);
+        System.setProperty(JAVA_HOME, createTempHome().getAbsolutePath());
+        handler = new JavaHomeHandler();
+        assertNotNull(handler);
+    }
+
+    public void testCreateHandler_Deviation() throws IOException {
+        JavaHomeHandler handler = new JavaHomeHandler(SOME_WEIRD_HOME);
+        assertNotNull(handler);
+        handler = new JavaHomeHandler(createTempHome().getAbsolutePath());
+        assertNotNull(handler);
+    }
+
+    public void testIsDeviation() throws IOException {
+        JavaHomeHandler handler = new JavaHomeHandler(createTempHome().getAbsolutePath());
+        assertTrue(handler.isDeviation());
+        handler = new JavaHomeHandler();
+        assertFalse(handler.isDeviation());
+        handler = new JavaHomeHandler(System.getProperty(JAVA_HOME));
+        assertFalse(handler.isDeviation());
+    }
+
+    public void testGetJavaHome() throws IOException {
+        String tempHome = createTempHome().getAbsolutePath();
+        JavaHomeHandler handler = new JavaHomeHandler(tempHome);
+        String home = handler.getHome().getAbsolutePath();
+        assertEquals(tempHome, home);
+        try {
+            handler = new JavaHomeHandler(SOME_WEIRD_HOME);
+        } catch (InstallerException ie) {
+            assertEquals("no valid java home", ie.getMessage());
+        }
+    }
+
+    public void testIsValidJavaHome() throws IOException {
+        JavaHomeHandler handler = new JavaHomeHandler(SOME_WEIRD_HOME);
+        assertFalse(handler.isValidHome());
+        handler = new JavaHomeHandler();
+        assertTrue(handler.isValidHome());
+        handler = new JavaHomeHandler(createTempHome().getAbsolutePath());
+        assertTrue(handler.isValidHome());
+    }
+
+    private File createTempHome() throws IOException {
+        File home = File.createTempFile("JavaHomeHandler", "Test");
+        assertTrue(FileHelper.createTempDirectory(home));
+        File binDir = new File(home, "bin");
+        assertTrue(binDir.mkdirs());
+        String executableName = JAVA;
+        if (Installation.isWindows()) {
+            executableName = executableName.concat(".exe");
+        }
+        File java = new File(binDir, executableName);
+        FileHelper.write(java, "dummy");
+        assertTrue(java.exists());
+        assertTrue(java.isFile());
+        return home;
+    }
+}
diff --git a/installer/test/java/org/python/util/install/JavaTest_Standalone.java b/installer/test/java/org/python/util/install/JavaTest_Standalone.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/JavaTest_Standalone.java
@@ -0,0 +1,32 @@
+package org.python.util.install;
+
+import org.python.util.install.Installation.JavaVersionInfo;
+
+/**
+ * Helper class to test an external java version
+ */
+public class JavaTest_Standalone {
+
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            JavaHomeHandler javaHomeHandler = new JavaHomeHandler(args[0]);
+            JavaVersionInfo versionInfo = Installation.getExternalJavaVersion(javaHomeHandler);
+            if (versionInfo.getErrorCode() != Installation.NORMAL_RETURN) {
+                System.err.println(versionInfo.getReason());
+            } else {
+                System.out.println(getPrefix() + "java version:" + versionInfo.getVersion());
+                System.out.println(getPrefix() + "java spec version:" + versionInfo.getSpecificationVersion());
+            }
+            System.exit(versionInfo.getErrorCode());
+        } else {
+            System.err.println(getPrefix() + "missing argument: please specify the java home directory "
+                    + "(/bin directory assumed below)");
+            System.exit(1);
+        }
+    }
+
+    private static String getPrefix() {
+        return "[JavaTest_Standalone] ";
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/StandalonePackagerTest.java b/installer/test/java/org/python/util/install/StandalonePackagerTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/StandalonePackagerTest.java
@@ -0,0 +1,183 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import junit.framework.TestCase;
+
+public class StandalonePackagerTest extends TestCase {
+    private File _sourceJarFile;
+    private File _targetJarFile;
+
+    private File _contentFile;
+    private File _nextContentFile;
+    private File _additionalFile;
+
+    private File _contentDir;
+    private File _additionalDir;
+
+    private File _jarDir;
+
+    protected void setUp() throws Exception {
+        _jarDir = File.createTempFile("jarDir", "");
+        assertTrue(FileHelper.createTempDirectory(_jarDir));
+        _targetJarFile = new File(_jarDir, "target.jar");
+        _sourceJarFile = new File(_jarDir, "source.jar");
+
+        _contentDir = File.createTempFile("content", "");
+        assertTrue(FileHelper.createTempDirectory(_contentDir));
+        _contentFile = new File(_contentDir, "content.file");
+        _contentFile.createNewFile();
+        assertTrue(_contentFile.exists());
+
+        createSourceJar();
+    }
+
+    protected void tearDown() throws Exception {
+        if (_sourceJarFile != null) {
+            _sourceJarFile.delete();
+        }
+        if (_targetJarFile != null) {
+            _targetJarFile.delete();
+        }
+        if (_contentFile != null) {
+            _contentFile.delete();
+        }
+        if (_nextContentFile != null) {
+            _nextContentFile.delete();
+        }
+        if (_additionalFile != null) {
+            _additionalFile.delete();
+        }
+        if (_contentDir != null) {
+            _contentDir.delete();
+        }
+        if (_additionalDir != null) {
+            _additionalDir.delete();
+        }
+        if (_jarDir != null) {
+            _jarDir.delete();
+        }
+    }
+
+    /**
+     * test the static method emptyDir()
+     */
+    public void testEmptyDir() throws Exception {
+        File tempContentFile = new File(_contentDir, "temp");
+        tempContentFile.createNewFile();
+        assertTrue(tempContentFile.exists());
+        File tempDir = new File(_contentDir, "tempDir");
+        assertTrue(FileHelper.createTempDirectory(tempDir));
+
+        StandalonePackager.emptyDirectory(_contentDir, _contentFile);
+        assertTrue(_contentFile.exists());
+        assertFalse(tempContentFile.exists());
+        assertFalse(tempDir.exists());
+        assertEquals(1, _contentDir.list().length);
+    }
+
+    /**
+     * test adding a jar file, a directory, and another single file
+     */
+    public void testAdd_Jar_Directory_File() throws IOException {
+        createAdditionalDirectory();
+        _nextContentFile = File.createTempFile("nextContent.file", "");
+        _nextContentFile.createNewFile();
+        assertTrue(_nextContentFile.exists());
+
+        StandalonePackager packager = new StandalonePackager(_targetJarFile);
+        try {
+            packager.addJarFile(_sourceJarFile);
+            packager.addFullDirectory(_additionalDir);
+            packager.addFile(_nextContentFile, null);
+        } finally {
+            packager.close();
+        }
+
+        assertTrue(_targetJarFile.exists());
+
+        Map<String, String> mandatoryEntries = new HashMap<String, String>(8);
+        mandatoryEntries.put(_contentDir.getName(), "dir");
+        mandatoryEntries.put(_contentFile.getName(), "file");
+        mandatoryEntries.put(_nextContentFile.getName(), "file");
+        mandatoryEntries.put(_additionalDir.getName(), "dir");
+        mandatoryEntries.put(_additionalFile.getName(), "file");
+        mandatoryEntries.put("META-INF", "dir");
+        mandatoryEntries.put("MANIFEST.MF", "file");
+
+        JarFile targetJarFile = new JarFile(_targetJarFile);
+        try {
+            Enumeration<JarEntry> entries = targetJarFile.entries();
+            while (entries.hasMoreElements()) {
+                JarEntry entry = (JarEntry) entries.nextElement();
+                String name;
+                String entryName = entry.getName();
+                int slashIndex = entryName.indexOf("/");
+                if (slashIndex >= 0) {
+                    // handle directory
+                    name = entryName.substring(slashIndex + 1);
+                    String dirName = entryName.substring(0, slashIndex);
+                    assertTrue(mandatoryEntries.containsKey(dirName));
+                    assertEquals("dir", mandatoryEntries.get(dirName));
+                    mandatoryEntries.remove(dirName);
+                } else {
+                    name = entryName;
+                }
+                if (mandatoryEntries.containsKey(name)) {
+                    assertEquals("file", (String) mandatoryEntries.get(name));
+                    assertFalse(entry.isDirectory());
+                    mandatoryEntries.remove(name);
+                }
+            }
+            assertTrue(mandatoryEntries.size() == 0);
+            assertNotNull(targetJarFile.getManifest());
+        } finally {
+            targetJarFile.close();
+        }
+    }
+
+    private void createSourceJar() throws FileNotFoundException, IOException {
+        Manifest manifest = new Manifest();
+        JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(_sourceJarFile), manifest);
+        addFile(_contentFile, _contentDir, jarOut);
+        jarOut.close();
+    }
+
+    private void createAdditionalDirectory() throws IOException {
+        _additionalDir = File.createTempFile("additional", "");
+        assertTrue(FileHelper.createTempDirectory(_additionalDir));
+
+        _additionalFile = new File(_additionalDir, "additional.file");
+        _additionalFile.createNewFile();
+        assertTrue(_additionalFile.exists());
+    }
+
+    private void addFile(File file, File parentDir, JarOutputStream jarOut) throws IOException {
+        byte[] buffer = new byte[1024];
+        FileInputStream inputStream = null;
+        try {
+            inputStream = new FileInputStream(file);
+            String jarEntryName = parentDir.getName() + "/" + file.getName();
+            jarOut.putNextEntry(new JarEntry(jarEntryName));
+            for (int read = 0; read != -1; read = inputStream.read(buffer))
+                jarOut.write(buffer, 0, read);
+            jarOut.closeEntry();
+        } finally {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        }
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/StartScriptGeneratorTest.java b/installer/test/java/org/python/util/install/StartScriptGeneratorTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/StartScriptGeneratorTest.java
@@ -0,0 +1,277 @@
+package org.python.util.install;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+public class StartScriptGeneratorTest extends TestCase {
+
+    private static final String AT_DATE = "@DATE";
+
+    private static final String WIN_CR_LF = StartScriptGenerator.WIN_CR_LF;
+
+    private StartScriptGenerator _generator;
+
+    private File _targetDir;
+
+    protected void setUp() throws Exception {
+        String userDirName = System.getProperty("user.dir"); // only true in eclipse ?
+        File userDir = new File(userDirName);
+        File parentDir = userDir.getParentFile();
+        assertTrue(parentDir.exists());
+        _targetDir = new File(parentDir, "jython");
+        if (!_targetDir.exists()) {
+            _targetDir = new File(parentDir, "jython-trunk");
+        }
+        assertTrue(_targetDir.exists());
+        assertTrue(_targetDir.isDirectory());
+        _targetDir = new File(_targetDir, "src");
+        _targetDir = new File(_targetDir, "shell");
+        assertTrue(_targetDir.exists());
+        assertTrue(_targetDir.isDirectory());
+        _generator = new StartScriptGenerator(_targetDir, new JavaHomeHandler());
+    }
+
+    // TODO: test on Solaris
+    public void testUnix() throws IOException {
+        _generator.setFlavour(StartScriptGenerator.UNIX_FLAVOUR);
+        StringBuffer buf = new StringBuffer(100);
+        buf.append("#!/usr/bin/env bash\n");
+        buf.append("\n");
+        buf.append("# This file was generated by the Jython installer\n");
+        buf.append("# Created on " + AT_DATE + " by " + System.getProperty("user.name") + "\n");
+        buf.append("\n");
+        buf.append("JAVA_HOME=\"");
+        buf.append(System.getProperty("java.home"));
+        buf.append("\"\n");
+        buf.append("JYTHON_HOME_FALLBACK=\"");
+        buf.append(_targetDir.getAbsolutePath());
+        buf.append("\"\n");
+        // some rudimentary tests - feel free to do more
+        String start = buf.toString().replaceAll(AT_DATE, new Date().toString());
+        String unixScript = _generator.getJythonScript(StartScriptGenerator.UNIX_FLAVOUR);
+        assertTrue(unixScript.startsWith(start));
+        assertTrue(unixScript.length() > 3500);
+        assertTrue(unixScript.indexOf("-Dpython.home=") > start.length());
+        assertTrue(unixScript.indexOf("-Dpython.executable=") > start.length());
+        // no hard coding of JYTHON_HOME
+        int jythonHomeIndex = unixScript.indexOf("if [ -z \"$JYTHON_HOME\" ] ; then");
+        assertTrue(jythonHomeIndex >= 0);
+        int definitionIndex = unixScript.indexOf("JYTHON_HOME=");
+        assertTrue(definitionIndex > jythonHomeIndex || definitionIndex < 0);
+    }
+
+    public void testWindows() throws IOException {
+        StringBuffer winBuf = new StringBuffer(100);
+        winBuf.append("@echo off" + WIN_CR_LF);
+        winBuf.append("rem This file was generated by the Jython installer" + WIN_CR_LF);
+        winBuf.append("rem Created on " + AT_DATE + " by " + System.getProperty("user.name") + ""
+                + WIN_CR_LF);
+        winBuf.append(WIN_CR_LF);
+        winBuf.append("set JAVA_HOME=\"");
+        winBuf.append(System.getProperty("java.home"));
+        winBuf.append("\"");
+        winBuf.append(WIN_CR_LF);
+        winBuf.append("set JYTHON_HOME_FALLBACK=\"");
+        winBuf.append(_targetDir.getAbsolutePath());
+        winBuf.append("\"");
+        winBuf.append(WIN_CR_LF);
+        // some rudimentary tests - feel free to do more
+        String start = winBuf.toString().replaceAll(AT_DATE, new Date().toString());
+        String winScript = _generator.getJythonScript(StartScriptGenerator.WINDOWS_FLAVOUR);
+        assertTrue(winScript.startsWith(start));
+        assertTrue(winScript.length() > 3500);
+        assertTrue(winScript.indexOf("if not \"%_TRIMMED_JAVA_HOME%\"==\"\"") > start.length());
+        assertTrue(winScript.indexOf("-Dpython.home=") > start.length());
+        assertTrue(winScript.indexOf("-Dpython.executable=") > start.length());
+        // no hard coding of JYTHON_HOME
+        int jythonHomeIndex = winScript.indexOf("if not \"%_TRIMMED_JYTHON_HOME%\"==\"\"");
+        assertTrue(jythonHomeIndex >= 0);
+        int definitionIndex = winScript.indexOf("set JYTHON_HOME=");
+        assertTrue(definitionIndex > jythonHomeIndex || definitionIndex < 0);
+    }
+
+    public void testFlavour() {
+        int expectedFlavour;
+        expectedFlavour = StartScriptGenerator.UNIX_FLAVOUR;
+        _generator.setFlavour(expectedFlavour);
+        assertEquals(expectedFlavour, _generator.getFlavour());
+        expectedFlavour = StartScriptGenerator.BOTH_FLAVOUR;
+        _generator.setFlavour(expectedFlavour);
+        assertEquals(expectedFlavour, _generator.getFlavour());
+        TestStartScriptGenerator testGenerator = new TestStartScriptGenerator(new File("dummy"),
+                                                                              new JavaHomeHandler("dummy"),
+                                                                              false);
+        expectedFlavour = StartScriptGenerator.WINDOWS_FLAVOUR;
+        testGenerator.setFlavour(expectedFlavour);
+        assertEquals(expectedFlavour, testGenerator.getFlavour());
+        expectedFlavour = StartScriptGenerator.UNIX_FLAVOUR;
+        testGenerator.setFlavour(expectedFlavour);
+        assertEquals(expectedFlavour, testGenerator.getFlavour());
+        testGenerator = new TestStartScriptGenerator(new File("dummy"),
+                                                     new JavaHomeHandler("dummy"),
+                                                     true);
+        testGenerator.setFlavour(StartScriptGenerator.WINDOWS_FLAVOUR);
+        assertEquals(StartScriptGenerator.BOTH_FLAVOUR, testGenerator.getFlavour());
+    }
+
+    public void testWindowsFlavour() throws IOException {
+        File dir = new File(System.getProperty("java.io.tmpdir"), "StartScriptGeneratorTest");
+        try {
+            if (!dir.exists()) {
+                assertTrue(dir.mkdirs());
+            }
+            File bin = new File(dir, "bin");
+            if (!bin.exists()) {
+                assertTrue(bin.mkdirs());
+            }
+            File jython = new File(bin, "jython");
+            if (!jython.exists()) {
+                assertTrue(jython.createNewFile());
+            }
+            File jython_bat = new File(bin, "jython.bat");
+            if (!jython_bat.exists()) {
+                assertTrue(jython_bat.createNewFile());
+            }
+            // windows flavour
+            TestStartScriptGenerator testGenerator = new TestStartScriptGenerator(dir,
+                                                                                  new JavaHomeHandler(),
+                                                                                  false);
+            testGenerator.setFlavour(StartScriptGenerator.WINDOWS_FLAVOUR);
+            testGenerator.generateStartScripts();
+            String[] fileNames = dir.list();
+            int fileNamesLength = fileNames.length;
+            assertEquals(2, fileNamesLength); // 1 file plus the /bin subdirectory
+            HashSet<String> fileNamesSet = new HashSet<String>(2);
+            for (int i = 0; i < fileNamesLength; i++) {
+                fileNamesSet.add(fileNames[i]);
+            }
+            assertTrue(fileNamesSet.contains("bin"));
+            assertTrue(fileNamesSet.contains("jython.bat"));
+            fileNames = bin.list();
+            assertEquals(1, fileNames.length);
+            assertEquals("jython.bat", fileNames[0]);
+        } finally {
+            if (dir.exists()) {
+                assertTrue("unable to delete directory ".concat(dir.getAbsolutePath()),
+                           FileHelper.rmdir(dir));
+            }
+        }
+    }
+
+    public void testUnixFlavour() throws IOException {
+        File dir = new File(System.getProperty("java.io.tmpdir"), "StartScriptGeneratorTest");
+        try {
+            if (!dir.exists()) {
+                assertTrue(dir.mkdirs());
+            }
+            File bin = new File(dir, "bin");
+            if (!bin.exists()) {
+                assertTrue(bin.mkdirs());
+            }
+            File jython = new File(bin, "jython");
+            if (!jython.exists()) {
+                assertTrue(jython.createNewFile());
+            }
+            File jython_bat = new File(bin, "jython.bat");
+            if (!jython_bat.exists()) {
+                assertTrue(jython_bat.createNewFile());
+            }
+            // unix flavour
+            TestStartScriptGenerator testGenerator = new TestStartScriptGenerator(dir,
+                                                                                  new JavaHomeHandler(),
+                                                                                  false);
+            testGenerator.setFlavour(StartScriptGenerator.UNIX_FLAVOUR);
+            testGenerator.generateStartScripts();
+            String[] fileNames = dir.list();
+            int fileNamesLength = fileNames.length;
+            assertEquals(2, fileNamesLength); // 1 file plus the /bin subdirectory
+            HashSet<String> fileNamesSet = new HashSet<String>(2);
+            for (int i = 0; i < fileNamesLength; i++) {
+                fileNamesSet.add(fileNames[i]);
+            }
+            assertTrue(fileNamesSet.contains("bin"));
+            assertTrue(fileNamesSet.contains("jython"));
+            fileNames = bin.list();
+            assertEquals(1, fileNames.length);
+            assertEquals("jython", fileNames[0]);
+        } finally {
+            if (dir.exists()) {
+                assertTrue("unable to delete directory ".concat(dir.getAbsolutePath()),
+                           FileHelper.rmdir(dir));
+            }
+        }
+    }
+
+    public void testBothFlavours() throws IOException {
+        File dir = new File(System.getProperty("java.io.tmpdir"), "StartScriptGeneratorTest");
+        try {
+            if (!dir.exists()) {
+                assertTrue(dir.mkdirs());
+            }
+            File bin = new File(dir, "bin");
+            if (!bin.exists()) {
+                assertTrue(bin.mkdirs());
+            }
+            File jython = new File(bin, "jython");
+            if (!jython.exists()) {
+                assertTrue(jython.createNewFile());
+            }
+            File jython_bat = new File(bin, "jython.bat");
+            if (!jython_bat.exists()) {
+                assertTrue(jython_bat.createNewFile());
+            }
+            // both flavours
+            TestStartScriptGenerator testGenerator = new TestStartScriptGenerator(dir,
+                                                                                  new JavaHomeHandler(),
+                                                                                  true);
+            // test generator constructor timing problem: do set the flavour once again
+            testGenerator.setFlavour(StartScriptGenerator.WINDOWS_FLAVOUR);
+            testGenerator.generateStartScripts();
+            String[] fileNames = dir.list();
+            int fileNamesLength = fileNames.length;
+            assertEquals(3, fileNamesLength); // 2 files plus the /bin subdirectory
+            Set<String> fileNamesSet = new HashSet<String>(4);
+            for (int i = 0; i < fileNamesLength; i++) {
+                fileNamesSet.add(fileNames[i]);
+            }
+            assertTrue(fileNamesSet.contains("bin"));
+            assertTrue(fileNamesSet.contains("jython"));
+            assertTrue(fileNamesSet.contains("jython.bat"));
+            fileNames = bin.list();
+            fileNamesLength = fileNames.length;
+            assertEquals(2, fileNamesLength);
+            fileNamesSet = new HashSet<String>(4);
+            for (int i = 0; i < fileNamesLength; i++) {
+                fileNamesSet.add(fileNames[i]);
+            }
+            assertTrue(fileNamesSet.contains("jython"));
+            assertTrue(fileNamesSet.contains("jython.bat"));
+        } finally {
+            if (dir.exists()) {
+                assertTrue("unable to delete directory ".concat(dir.getAbsolutePath()),
+                           FileHelper.rmdir(dir));
+            }
+        }
+    }
+
+    class TestStartScriptGenerator extends StartScriptGenerator {
+
+        private boolean _hasBothFlavours;
+
+        public TestStartScriptGenerator(File targetDirectory,
+                                        JavaHomeHandler javaHomeHandler,
+                                        boolean hasBothFlavours) {
+            super(targetDirectory, javaHomeHandler);
+            _hasBothFlavours = hasBothFlavours;
+        }
+
+        protected boolean hasUnixlikeShell() {
+            return _hasBothFlavours;
+        }
+    }
+}
diff --git a/installer/test/java/org/python/util/install/UnicodeSequencesTest.java b/installer/test/java/org/python/util/install/UnicodeSequencesTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/UnicodeSequencesTest.java
@@ -0,0 +1,34 @@
+package org.python.util.install;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+public class UnicodeSequencesTest extends TestCase {
+
+    private static Set _latin1Encodings;
+
+    public void testUmlaute() {
+        String fileEncoding = System.getProperty("file.encoding", "unknown");
+        if (getLatin1Encodings().contains(fileEncoding)) {
+            assertEquals("ä", UnicodeSequences.a2);
+            assertEquals("Ä", UnicodeSequences.A2);
+            assertEquals("ö", UnicodeSequences.o2);
+            assertEquals("Ö", UnicodeSequences.O2);
+            assertEquals("ü", UnicodeSequences.u2);
+            assertEquals("Ü", UnicodeSequences.U2);
+        }
+    }
+
+    private static Set getLatin1Encodings() {
+        if (_latin1Encodings == null) {
+            _latin1Encodings = new HashSet(3);
+            _latin1Encodings.add("ISO-LATIN-1");
+            _latin1Encodings.add("ISO-8859-1");
+            _latin1Encodings.add("Cp1252");
+        }
+        return _latin1Encodings;
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/driver/AutotestTest.java b/installer/test/java/org/python/util/install/driver/AutotestTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/driver/AutotestTest.java
@@ -0,0 +1,78 @@
+package org.python.util.install.driver;
+
+import java.io.File;
+
+import org.python.util.install.InstallerCommandLine;
+
+import junit.framework.TestCase;
+
+public class AutotestTest extends TestCase {
+
+    private Autotest _autotest;
+
+    protected void setUp() throws Exception {
+        InstallerCommandLine commandLine = new InstallerCommandLine();
+        commandLine.setArgs(new String[0]);
+        _autotest = new SilentAutotest(commandLine);
+    }
+
+    public void testCreateDirectories() {
+        File rootDir = Autotest.getRootDir();
+        File targetDir = _autotest.getTargetDir();
+        assertNotNull(rootDir);
+        verifyDir(rootDir, false);
+        assertNotNull(targetDir);
+        verifyDir(targetDir, true);
+        assertEquals(rootDir, targetDir.getParentFile());
+    }
+
+    public void testCommandLineArgs() {
+        String[] args = new String[] { "-x", "-y", "-z" };
+        _autotest.setCommandLineArgs(args);
+        int len = _autotest.getCommandLineArgs().length;
+        assertEquals(args.length, len);
+        for (int i = 0; i < args.length; i++) {
+            assertEquals(args[i], _autotest.getCommandLineArgs()[i]);
+        }
+    }
+
+    public void testAddArgument() {
+        String[] args = new String[] { "-x", "-y", "-z" };
+        _autotest.setCommandLineArgs(args);
+        _autotest.addArgument("-u");
+        assertEquals(args.length + 1, _autotest.getCommandLineArgs().length);
+        for (int i = 0; i < args.length; i++) {
+            assertEquals(args[i], _autotest.getCommandLineArgs()[i]);
+        }
+        assertEquals("-u", _autotest.getCommandLineArgs()[args.length]);
+    }
+
+    public void testVerify() throws Exception {
+        TestVerifier testVerifier = new TestVerifier();
+        _autotest.setVerifier(testVerifier);
+        assertNotNull(_autotest.getVerifier());
+        assertNotNull(_autotest.getVerifier().getTargetDir());
+        assertEquals(_autotest.getTargetDir(), testVerifier.getTargetDir());
+        try {
+            _autotest.getVerifier().verify();
+            fail("should have thrown");
+        } catch (DriverException de) {
+        }
+
+    }
+
+    private void verifyDir(File dir, boolean ensureEmpty) {
+        assertTrue(dir.exists());
+        assertTrue(dir.isDirectory());
+        if (ensureEmpty) {
+            assertTrue(dir.listFiles().length <= 0);
+        }
+    }
+
+    private static class TestVerifier extends NormalVerifier {
+        public void verify() throws DriverException {
+            throw new DriverException("test verification failure");
+        }
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/driver/DrivableConsole.java b/installer/test/java/org/python/util/install/driver/DrivableConsole.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/driver/DrivableConsole.java
@@ -0,0 +1,82 @@
+package org.python.util.install.driver;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.python.util.install.driver.Tunnel;
+
+/**
+ * A simple class performing console I/O, easy to test.
+ */
+public class DrivableConsole {
+
+    private static final String _PROMPT = ">>>";
+    private Tunnel _tunnel;
+
+    public DrivableConsole(Tunnel tunnel) {
+        _tunnel = tunnel;
+    }
+
+    /**
+     * The console logic.
+     */
+    public void handleConsoleIO() throws Exception {
+        String answer;
+        answer = question("first question");
+        if ("1".equals(answer)) {
+            System.out.println("answer1 is " + answer);
+            answer = question("second question");
+            if ("2".equals(answer)) {
+                System.out.println("answer2 is " + answer);
+                answer = question("third question");
+                if ("3".equals(answer)) {
+                    System.out.println("answer3 is " + answer);
+                } else {
+                    throw new Exception("wrong answer3: " + answer);
+                }
+            } else {
+                throw new Exception("wrong answer2: " + answer);
+            }
+        } else {
+            throw new Exception("wrong answer1: " + answer);
+        }
+    }
+
+    /**
+     * Write a question (to normal <code>System.out</code>)
+     */
+    private String question(String question) throws IOException {
+        question = question + " " + _PROMPT + " ";
+        String answer = "";
+        // output to normal System.out
+        System.out.print(question); // intended print, not println (!)
+        answer = readLine();
+        return answer;
+    }
+
+    /**
+     * Send a signal through the tunnel, and then wait for the answer from the other side.
+     * 
+     * <pre>
+     *     (2)  [Driver]   receives question  [Tunnel]   sends question   [Console]  (1)
+     *     (3)  [Driver]   sends answer       [Tunnel]   receives answer  [Console]  (4)
+     * </pre>
+     */
+    private String readLine() throws IOException {
+        InputStream inputStream;
+        String line = "";
+        if (_tunnel == null) {
+            inputStream = System.in;
+        } else {
+            inputStream = _tunnel.getAnswerReceiverStream();
+            _tunnel.getQuestionSenderStream().write(Tunnel.NEW_LINE.getBytes());
+            _tunnel.getQuestionSenderStream().flush();
+        }
+        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+        line = reader.readLine();
+        return line;
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/driver/DrivableConsoleTest.java b/installer/test/java/org/python/util/install/driver/DrivableConsoleTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/driver/DrivableConsoleTest.java
@@ -0,0 +1,37 @@
+package org.python.util.install.driver;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.python.util.install.driver.ConsoleDriver;
+import org.python.util.install.driver.Tunnel;
+
+import junit.framework.TestCase;
+
+public class DrivableConsoleTest extends TestCase {
+
+    private DrivableConsole _console;
+    private Tunnel _tunnel;
+
+    protected void setUp() throws IOException {
+        _tunnel = new Tunnel();
+        _console = new DrivableConsole(_tunnel);
+    }
+
+    public void testDrive() throws Exception {
+        // sequence matters here (have to fork off the driver thread first
+        ConsoleDriver driver = new ConsoleDriver(_tunnel, getAnswers());
+        driver.start();
+        _console.handleConsoleIO();
+    }
+
+    private Collection getAnswers() {
+        Collection answers = new ArrayList();
+        answers.add("1");
+        answers.add("2");
+        answers.add("3");
+        return answers;
+    }
+
+}
diff --git a/installer/test/java/org/python/util/install/driver/NormalVerifierTest.java b/installer/test/java/org/python/util/install/driver/NormalVerifierTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/driver/NormalVerifierTest.java
@@ -0,0 +1,131 @@
+package org.python.util.install.driver;
+
+import java.io.File;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.python.util.install.FileHelper;
+import org.python.util.install.Installation;
+import org.python.util.install.JavaVersionTester;
+
+public class NormalVerifierTest extends TestCase {
+
+    private static final String DQ = "\"";
+
+    private NormalVerifier _verifier;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        _verifier = new NormalVerifier();
+        // use a directory containing spaces as target directory
+        File targetDir = createTargetDirectory();
+        assertTrue(targetDir.exists());
+        assertTrue(targetDir.isDirectory());
+        _verifier.setTargetDir(targetDir);
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        if (_verifier.getTargetDir() != null) {
+            File autotestFile = new File(_verifier.getTargetDir().getCanonicalPath(),
+                                         NormalVerifier.AUTOTEST_PY);
+            if (autotestFile.exists()) {
+                assertTrue(autotestFile.delete());
+            }
+        }
+    }
+
+    // have to install jython first in order to activate this test
+    public void testVerify() throws Exception {}
+
+    public void testGetSimpleCommand() throws Exception {
+        String prefix = _verifier.getTargetDir().getCanonicalPath().concat(File.separator);
+        String expectedCommand = prefix.concat("jython");
+        if (Installation.isWindows()) {
+            expectedCommand = expectedCommand.concat(".bat");
+        }
+        String expectedArgument = prefix.concat("autotest.py");
+        String[] command = _verifier.getSimpleCommand();
+        assertNotNull(command);
+        assertEquals(2, command.length);
+        assertEquals(expectedCommand, command[0]);
+        assertEquals(expectedArgument, command[1]);
+    }
+
+    public void testDoShellScriptTests() {
+        assertTrue(_verifier.doShellScriptTests());
+    }
+
+    public void testGetShellScriptTestCommandDir() throws DriverException, IOException {
+        String expectedDir = _verifier.getTargetDir()
+                .getCanonicalPath()
+                .concat(File.separator)
+                .concat("bin");
+        assertEquals(expectedDir, _verifier.getShellScriptTestCommandDir().getCanonicalPath());
+    }
+
+    public void testGetShellScriptTestContents() throws Exception {
+        String contents = _verifier.getShellScriptTestContents();
+        // common asserts
+        assertNotNull(contents);
+        assertFalse(contents.length() == 0);
+        assertFalse(contents.indexOf("{0}") > 0);
+        assertFalse(contents.indexOf("{1}") > 0);
+        assertFalse(contents.indexOf("{2}") > 0);
+        assertFalse(contents.indexOf("{3}") > 0);
+        assertTrue(contents.indexOf("autotest.py") > 0);
+        String targetDirPath = _verifier.getTargetDir().getCanonicalPath();
+        String upScriptPath = _verifier.getSimpleCommand()[1];
+        String javaHome = System.getProperty(JavaVersionTester.JAVA_HOME, ""); // change this ++++++
+        assertTrue(javaHome.length() > 0);
+        // platform specific asserts
+        if (Installation.isWindows()) {
+            assertTrue(contents.indexOf("set _INSTALL_DIR=") > 0);
+            assertTrue(contents.indexOf("set _INSTALL_DIR=".concat(targetDirPath)) > 0);
+            assertTrue(contents.indexOf("set _SCRIPT=") > 0);
+            assertTrue(contents.indexOf("set _SCRIPT=".concat(upScriptPath)) > 0);
+            assertTrue(contents.indexOf("set _JAVA_HOME=") > 0);
+            assertTrue(contents.indexOf("set _JAVA_HOME=".concat(javaHome)) > 0);
+        } else {
+            System.out.println(contents);
+            assertTrue(contents.indexOf("_INSTALL_DIR=") > 0);
+            assertTrue(contents.indexOf("_INSTALL_DIR=".concat(quote(targetDirPath))) > 0);
+            assertTrue(contents.indexOf("_SCRIPT=") > 0);
+            assertTrue(contents.indexOf("_SCRIPT=".concat(quote(upScriptPath))) > 0);
+            assertTrue(contents.indexOf("_JAVA_HOME=") > 0);
+            assertTrue(contents.indexOf("_JAVA_HOME=".concat(quote(javaHome))) > 0);
+        }
+    }
+
+    public void testGetShellScriptTestCommand() throws Exception {
+        String prefix = _verifier.getShellScriptTestCommandDir()
+                .getCanonicalPath()
+                .concat(File.separator);
+        String expectedCommand = prefix.concat("jython_test");
+        if (Installation.isWindows()) {
+            expectedCommand = expectedCommand.concat(".bat");
+        }
+        String[] command = _verifier.getShellScriptTestCommand();
+        assertNotNull(command);
+        assertEquals(1, command.length);
+        String commandFileName = command[0];
+        assertEquals(expectedCommand, commandFileName);
+        File commandFile = new File(commandFileName);
+        assertTrue(commandFile.exists());
+        String contents = FileHelper.readAll(commandFile);
+        assertNotNull(contents);
+        assertFalse(contents.length() == 0);
+        assertEquals(_verifier.getShellScriptTestContents(), contents);
+    }
+
+    private File createTargetDirectory() throws IOException {
+        File tmpFile = File.createTempFile("NormalVerifierTest_", "with spaces");
+        FileHelper.createTempDirectory(tmpFile);
+        return tmpFile;
+    }
+
+    private String quote(String value) {
+        return DQ.concat(value).concat(DQ);
+    }
+}
diff --git a/installer/test/java/org/python/util/install/driver/StandaloneVerifierTest.java b/installer/test/java/org/python/util/install/driver/StandaloneVerifierTest.java
new file mode 100644
--- /dev/null
+++ b/installer/test/java/org/python/util/install/driver/StandaloneVerifierTest.java
@@ -0,0 +1,72 @@
+package org.python.util.install.driver;
+
+import java.io.File;
+
+import org.python.util.install.Installation;
+import org.python.util.install.JarInstaller;
+
+import junit.framework.TestCase;
+
+public class StandaloneVerifierTest extends TestCase {
+
+    private StandaloneVerifier _verifier;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        _verifier = new StandaloneVerifier();
+        File targetDir = null;
+        // have to install jython first in order to activate this test
+        // targetDir = new File("C:/Temp/jython.autoinstall.root_54159_dir/006
+        // consoleTest_54165_dir");
+        _verifier.setTargetDir(targetDir);
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        if (_verifier.getTargetDir() != null) {
+            File autotestFile = new File(_verifier.getTargetDir().getCanonicalPath(),
+                                         StandaloneVerifier.AUTOTEST_PY);
+            if (autotestFile.exists()) {
+                assertTrue(autotestFile.delete());
+            }
+        }
+    }
+
+    public void testVerify() throws Exception {
+        if (_verifier.getTargetDir() != null) {
+            _verifier.verify();
+        }
+    }
+
+    public void testGetSimpleCommand() throws Exception {
+        File javaHome = new File(System.getProperty("java.home"));
+        assertNotNull(javaHome);
+        assertTrue(javaHome.exists());
+        File targetDir = new File(System.getProperty(("user.dir"))); // any existing dir
+        assertNotNull(targetDir);
+        assertTrue(targetDir.exists());
+        String prefix = targetDir.getCanonicalPath().concat(File.separator);
+        String expectedCommand = javaHome.getCanonicalPath()
+                .concat(File.separator)
+                .concat("bin")
+                .concat(File.separator)
+                .concat("java");
+        if (Installation.isWindows()) {
+            expectedCommand = expectedCommand.concat(".exe");
+        }
+        String expectedArgument = prefix.concat("autotest.py");
+        _verifier.setTargetDir(targetDir);
+        String[] command = _verifier.getSimpleCommand();
+        assertNotNull(command);
+        assertEquals(4, command.length);
+        assertEquals(expectedCommand, command[0]);
+        assertEquals("-jar", command[1]);
+        assertEquals(prefix.concat(JarInstaller.JYTHON_JAR), command[2]);
+        assertEquals(expectedArgument, command[3]);
+    }
+
+    public void testDoShellScriptTests() {
+        // we cannot do shell script tests in standalone mode
+        assertFalse(_verifier.doShellScriptTests());
+    }
+}
diff --git a/lib-python/2.7/BaseHTTPServer.py b/lib-python/2.7/BaseHTTPServer.py
--- a/lib-python/2.7/BaseHTTPServer.py
+++ b/lib-python/2.7/BaseHTTPServer.py
@@ -447,13 +447,13 @@
         specified as subsequent arguments (it's just like
         printf!).
 
-        The client host and current date/time are prefixed to
-        every message.
+        The client ip address and current date/time are prefixed to every
+        message.
 
         """
 
         sys.stderr.write("%s - - [%s] %s\n" %
-                         (self.address_string(),
+                         (self.client_address[0],
                           self.log_date_time_string(),
                           format%args))
 
diff --git a/lib-python/2.7/CGIHTTPServer.py b/lib-python/2.7/CGIHTTPServer.py
--- a/lib-python/2.7/CGIHTTPServer.py
+++ b/lib-python/2.7/CGIHTTPServer.py
@@ -84,9 +84,11 @@
         path begins with one of the strings in self.cgi_directories
         (and the next character is a '/' or the end of the string).
         """
-        splitpath = _url_collapse_path_split(self.path)
-        if splitpath[0] in self.cgi_directories:
-            self.cgi_info = splitpath
+        collapsed_path = _url_collapse_path(self.path)
+        dir_sep = collapsed_path.find('/', 1)
+        head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
+        if head in self.cgi_directories:
+            self.cgi_info = head, tail
             return True
         return False
 
@@ -298,51 +300,46 @@
                 self.log_message("CGI script exited OK")
 
 
-# TODO(gregory.p.smith): Move this into an appropriate library.
-def _url_collapse_path_split(path):
+def _url_collapse_path(path):
     """
     Given a URL path, remove extra '/'s and '.' path elements and collapse
-    any '..' references.
+    any '..' references and returns a colllapsed path.
 
     Implements something akin to RFC-2396 5.2 step 6 to parse relative paths.
+    The utility of this function is limited to is_cgi method and helps
+    preventing some security attacks.
 
     Returns: A tuple of (head, tail) where tail is everything after the final /
     and head is everything before it.  Head will always start with a '/' and,
     if it contains anything else, never have a trailing '/'.
 
     Raises: IndexError if too many '..' occur within the path.
+
     """
     # Similar to os.path.split(os.path.normpath(path)) but specific to URL
     # path semantics rather than local operating system semantics.
-    path_parts = []
-    for part in path.split('/'):
-        if part == '.':
-            path_parts.append('')
-        else:
-            path_parts.append(part)
-    # Filter out blank non trailing parts before consuming the '..'.
-    path_parts = [part for part in path_parts[:-1] if part] + path_parts[-1:]
+    path_parts = path.split('/')
+    head_parts = []
+    for part in path_parts[:-1]:
+        if part == '..':
+            head_parts.pop() # IndexError if more '..' than prior parts
+        elif part and part != '.':
+            head_parts.append( part )
     if path_parts:
-        # Special case for CGI's for PATH_INFO
-        if path.startswith('/cgi-bin') or path.startswith('/htbin'):
-            tail_part = []
-            while path_parts[-1] not in ('cgi-bin','htbin'):
-                tail_part.insert(0,path_parts.pop())
-            tail_part = "/".join(tail_part)
-        else:
-            tail_part = path_parts.pop()
+        tail_part = path_parts.pop()
+        if tail_part:
+            if tail_part == '..':
+                head_parts.pop()
+                tail_part = ''
+            elif tail_part == '.':
+                tail_part = ''
     else:
         tail_part = ''
-    head_parts = []
-    for part in path_parts:
-        if part == '..':
-            head_parts.pop()
-        else:
-            head_parts.append(part)
-    if tail_part and tail_part == '..':
-        head_parts.pop()
-        tail_part = ''
-    return ('/' + '/'.join(head_parts), tail_part)
+
+    splitpath = ('/' + '/'.join(head_parts), tail_part)
+    collapsed_path = "/".join(splitpath)
+
+    return collapsed_path
 
 
 nobody = None
diff --git a/lib-python/2.7/Cookie.py b/lib-python/2.7/Cookie.py
--- a/lib-python/2.7/Cookie.py
+++ b/lib-python/2.7/Cookie.py
@@ -390,7 +390,7 @@
     from time import gmtime, time
     now = time()
     year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
-    return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
+    return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \
            (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
 
 
@@ -539,7 +539,7 @@
     r"(?P<val>"                   # Start of group 'val'
     r'"(?:[^\\"]|\\.)*"'            # Any doublequoted string
     r"|"                            # or
-    r"\w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
+    r"\w{3},\s[\s\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
     r"|"                            # or
     ""+ _LegalCharsPatt +"*"        # Any word or empty string
     r")"                          # End of group 'val'
diff --git a/lib-python/2.7/HTMLParser.py b/lib-python/2.7/HTMLParser.py
--- a/lib-python/2.7/HTMLParser.py
+++ b/lib-python/2.7/HTMLParser.py
@@ -22,13 +22,13 @@
 starttagopen = re.compile('<[a-zA-Z]')
 piclose = re.compile('>')
 commentclose = re.compile(r'--\s*>')
-tagfind = re.compile('[a-zA-Z][-.a-zA-Z0-9:_]*')
+tagfind = re.compile('([a-zA-Z][-.a-zA-Z0-9:_]*)(?:\s|/(?!>))*')
 # see http://www.w3.org/TR/html5/tokenization.html#tag-open-state
 # and http://www.w3.org/TR/html5/tokenization.html#tag-name-state
 tagfind_tolerant = re.compile('[a-zA-Z][^\t\n\r\f />\x00]*')
 
 attrfind = re.compile(
-    r'[\s/]*((?<=[\'"\s/])[^\s/>][^\s/=>]*)(\s*=+\s*'
+    r'((?<=[\'"\s/])[^\s/>][^\s/=>]*)(\s*=+\s*'
     r'(\'[^\']*\'|"[^"]*"|(?![\'"])[^>\s]*))?(?:\s|/(?!>))*')
 
 locatestarttagend = re.compile(r"""
@@ -289,7 +289,7 @@
         match = tagfind.match(rawdata, i+1)
         assert match, 'unexpected call to parse_starttag()'
         k = match.end()
-        self.lasttag = tag = rawdata[i+1:k].lower()
+        self.lasttag = tag = match.group(1).lower()
 
         while k < endpos:
             m = attrfind.match(rawdata, k)
diff --git a/lib-python/2.7/SocketServer.py b/lib-python/2.7/SocketServer.py
--- a/lib-python/2.7/SocketServer.py
+++ b/lib-python/2.7/SocketServer.py
@@ -133,6 +133,7 @@
 import select
 import sys
 import os
+import errno
 try:
     import threading
 except ImportError:
@@ -147,6 +148,15 @@
                     "ThreadingUnixStreamServer",
                     "ThreadingUnixDatagramServer"])
 
+def _eintr_retry(func, *args):
+    """restart a system call interrupted by EINTR"""
+    while True:
+        try:
+            return func(*args)
+        except (OSError, select.error) as e:
+            if e.args[0] != errno.EINTR:
+                raise
+
 class BaseServer:
 
     """Base class for server classes.
@@ -222,7 +232,8 @@
                 # connecting to the socket to wake this up instead of
                 # polling. Polling reduces our responsiveness to a
                 # shutdown request and wastes cpu at all other times.
-                r, w, e = select.select([self], [], [], poll_interval)
+                r, w, e = _eintr_retry(select.select, [self], [], [],
+                                       poll_interval)
                 if self in r:
                     self._handle_request_noblock()
         finally:
@@ -262,7 +273,7 @@
             timeout = self.timeout
         elif self.timeout is not None:
             timeout = min(timeout, self.timeout)
-        fd_sets = select.select([self], [], [], timeout)
+        fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
         if not fd_sets[0]:
             self.handle_timeout()
             return
@@ -690,7 +701,12 @@
 
     def finish(self):
         if not self.wfile.closed:
-            self.wfile.flush()
+            try:
+                self.wfile.flush()
+            except socket.error:
+                # An final socket error may have occurred here, such as
+                # the local error ECONNABORTED.
+                pass
         self.wfile.close()
         self.rfile.close()
 
diff --git a/lib-python/2.7/StringIO.py b/lib-python/2.7/StringIO.py
--- a/lib-python/2.7/StringIO.py
+++ b/lib-python/2.7/StringIO.py
@@ -158,7 +158,7 @@
             newpos = self.len
         else:
             newpos = i+1
-        if length is not None and length > 0:
+        if length is not None and length >= 0:
             if self.pos + length < newpos:
                 newpos = self.pos + length
         r = self.buf[self.pos:newpos]
diff --git a/lib-python/2.7/_LWPCookieJar.py b/lib-python/2.7/_LWPCookieJar.py
--- a/lib-python/2.7/_LWPCookieJar.py
+++ b/lib-python/2.7/_LWPCookieJar.py
@@ -48,7 +48,7 @@
 
 class LWPCookieJar(FileCookieJar):
     """
-    The LWPCookieJar saves a sequence of"Set-Cookie3" lines.
+    The LWPCookieJar saves a sequence of "Set-Cookie3" lines.
     "Set-Cookie3" is the format used by the libwww-perl libary, not known
     to be compatible with any browser, but which is easy to read and
     doesn't lose information about RFC 2965 cookies.
@@ -60,7 +60,7 @@
     """
 
     def as_lwp_str(self, ignore_discard=True, ignore_expires=True):
-        """Return cookies as a string of "\n"-separated "Set-Cookie3" headers.
+        """Return cookies as a string of "\\n"-separated "Set-Cookie3" headers.
 
         ignore_discard and ignore_expires: see docstring for FileCookieJar.save
 
diff --git a/lib-python/2.7/__future__.py b/lib-python/2.7/__future__.py
--- a/lib-python/2.7/__future__.py
+++ b/lib-python/2.7/__future__.py
@@ -112,7 +112,7 @@
                     CO_FUTURE_DIVISION)
 
 absolute_import = _Feature((2, 5, 0, "alpha", 1),
-                           (2, 7, 0, "alpha", 0),
+                           (3, 0, 0, "alpha", 0),
                            CO_FUTURE_ABSOLUTE_IMPORT)
 
 with_statement = _Feature((2, 5, 0, "alpha", 1),
diff --git a/lib-python/2.7/_osx_support.py b/lib-python/2.7/_osx_support.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/_osx_support.py
@@ -0,0 +1,488 @@
+"""Shared OS X support functions."""
+
+import os
+import re
+import sys
+
+__all__ = [
+    'compiler_fixup',
+    'customize_config_vars',
+    'customize_compiler',
+    'get_platform_osx',
+]
+
+# configuration variables that may contain universal build flags,
+# like "-arch" or "-isdkroot", that may need customization for
+# the user environment
+_UNIVERSAL_CONFIG_VARS = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS', 'BASECFLAGS',
+                            'BLDSHARED', 'LDSHARED', 'CC', 'CXX',
+                            'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS',
+                            'PY_CORE_CFLAGS')
+
+# configuration variables that may contain compiler calls
+_COMPILER_CONFIG_VARS = ('BLDSHARED', 'LDSHARED', 'CC', 'CXX')
+
+# prefix added to original configuration variable names
+_INITPRE = '_OSX_SUPPORT_INITIAL_'
+
+
+def _find_executable(executable, path=None):
+    """Tries 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.
+    """
+    if path is None:
+        path = os.environ['PATH']
+
+    paths = path.split(os.pathsep)
+    base, ext = os.path.splitext(executable)
+
+    if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'):
+        executable = executable + '.exe'
+
+    if not os.path.isfile(executable):
+        for p in paths:
+            f = os.path.join(p, executable)
+            if os.path.isfile(f):
+                # the file exists, we have a shot at spawn working
+                return f
+        return None
+    else:
+        return executable
+
+
+def _read_output(commandstring):
+    """Output from succesful command execution or None"""
+    # Similar to os.popen(commandstring, "r").read(),
+    # but without actually using os.popen because that
+    # function is not usable during python bootstrap.
+    # tempfile is also not available then.
+    import contextlib
+    try:
+        import tempfile
+        fp = tempfile.NamedTemporaryFile()
+    except ImportError:
+        fp = open("/tmp/_osx_support.%s"%(
+            os.getpid(),), "w+b")
+
+    with contextlib.closing(fp) as fp:
+        cmd = "%s 2>/dev/null >'%s'" % (commandstring, fp.name)
+        return fp.read().decode('utf-8').strip() if not os.system(cmd) else None
+
+
+def _find_build_tool(toolname):
+    """Find a build tool on current path or using xcrun"""
+    return (_find_executable(toolname)
+                or _read_output("/usr/bin/xcrun -find %s" % (toolname,))
+                or ''
+            )
+
+_SYSTEM_VERSION = None
+
+def _get_system_version():
+    """Return the OS X system version as a string"""
+    # Reading this plist is a documented way to get the system
+    # version (see the documentation for the Gestalt Manager)
+    # We avoid using platform.mac_ver to avoid possible bootstrap issues during
+    # the build of Python itself (distutils is used to build standard library
+    # extensions).
+
+    global _SYSTEM_VERSION
+
+    if _SYSTEM_VERSION is None:
+        _SYSTEM_VERSION = ''
+        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:
+                _SYSTEM_VERSION = '.'.join(m.group(1).split('.')[:2])
+            # else: fall back to the default behaviour
+
+    return _SYSTEM_VERSION
+
+def _remove_original_values(_config_vars):
+    """Remove original unmodified values for testing"""
+    # This is needed for higher-level cross-platform tests of get_platform.
+    for k in list(_config_vars):
+        if k.startswith(_INITPRE):
+            del _config_vars[k]
+
+def _save_modified_value(_config_vars, cv, newvalue):
+    """Save modified and original unmodified value of configuration var"""
+
+    oldvalue = _config_vars.get(cv, '')
+    if (oldvalue != newvalue) and (_INITPRE + cv not in _config_vars):
+        _config_vars[_INITPRE + cv] = oldvalue
+    _config_vars[cv] = newvalue
+
+def _supports_universal_builds():
+    """Returns True if universal builds are supported on this system"""
+    # As an approximation, we assume that if we are running on 10.4 or above,
+    # then we are running with an Xcode environment that supports universal
+    # builds, in particular -isysroot and -arch arguments to the compiler. This
+    # is in support of allowing 10.4 universal builds to run on 10.3.x systems.
+
+    osx_version = _get_system_version()
+    if osx_version:
+        try:
+            osx_version = tuple(int(i) for i in osx_version.split('.'))
+        except ValueError:
+            osx_version = ''
+    return bool(osx_version >= (10, 4)) if osx_version else False
+
+
+def _find_appropriate_compiler(_config_vars):
+    """Find appropriate C compiler for extension module builds"""
+
+    # Issue #13590:
+    #    The OSX location for the compiler varies between OSX
+    #    (or rather Xcode) releases.  With older releases (up-to 10.5)
+    #    the compiler is in /usr/bin, with newer releases the compiler
+    #    can only be found inside Xcode.app if the "Command Line Tools"
+    #    are not installed.
+    #
+    #    Futhermore, the compiler that can be used varies between
+    #    Xcode releases. Upto Xcode 4 it was possible to use 'gcc-4.2'
+    #    as the compiler, after that 'clang' should be used because
+    #    gcc-4.2 is either not present, or a copy of 'llvm-gcc' that
+    #    miscompiles Python.
+
+    # skip checks if the compiler was overriden with a CC env variable
+    if 'CC' in os.environ:
+        return _config_vars
+
+    # The CC config var might contain additional arguments.
+    # Ignore them while searching.
+    cc = oldcc = _config_vars['CC'].split()[0]
+    if not _find_executable(cc):
+        # Compiler is not found on the shell search PATH.
+        # Now search for clang, first on PATH (if the Command LIne
+        # Tools have been installed in / or if the user has provided
+        # another location via CC).  If not found, try using xcrun
+        # to find an uninstalled clang (within a selected Xcode).
+
+        # NOTE: Cannot use subprocess here because of bootstrap
+        # issues when building Python itself (and os.popen is
+        # implemented on top of subprocess and is therefore not
+        # usable as well)
+
+        cc = _find_build_tool('clang')
+
+    elif os.path.basename(cc).startswith('gcc'):
+        # Compiler is GCC, check if it is LLVM-GCC
+        data = _read_output("'%s' --version"
+                             % (cc.replace("'", "'\"'\"'"),))
+        if 'llvm-gcc' in data:
+            # Found LLVM-GCC, fall back to clang
+            cc = _find_build_tool('clang')
+
+    if not cc:
+        raise SystemError(
+               "Cannot locate working compiler")
+
+    if cc != oldcc:
+        # Found a replacement compiler.
+        # Modify config vars using new compiler, if not already explictly
+        # overriden by an env variable, preserving additional arguments.
+        for cv in _COMPILER_CONFIG_VARS:
+            if cv in _config_vars and cv not in os.environ:
+                cv_split = _config_vars[cv].split()
+                cv_split[0] = cc if cv != 'CXX' else cc + '++'
+                _save_modified_value(_config_vars, cv, ' '.join(cv_split))
+
+    return _config_vars
+
+
+def _remove_universal_flags(_config_vars):
+    """Remove all universal build arguments from config vars"""
+
+    for cv in _UNIVERSAL_CONFIG_VARS:
+        # Do not alter a config var explicitly overriden by env var
+        if cv in _config_vars and cv not in os.environ:
+            flags = _config_vars[cv]
+            flags = re.sub('-arch\s+\w+\s', ' ', flags)
+            flags = re.sub('-isysroot [^ \t]*', ' ', flags)
+            _save_modified_value(_config_vars, cv, flags)
+
+    return _config_vars
+
+
+def _remove_unsupported_archs(_config_vars):
+    """Remove any unsupported archs from config vars"""
+    # Different Xcode releases support different sets for '-arch'
+    # flags. In particular, Xcode 4.x no longer supports the
+    # PPC architectures.
+    #
+    # This code automatically removes '-arch ppc' and '-arch ppc64'
+    # when these are not supported. That makes it possible to
+    # build extensions on OSX 10.7 and later with the prebuilt
+    # 32-bit installer on the python.org website.
+
+    # skip checks if the compiler was overriden with a CC env variable
+    if 'CC' in os.environ:
+        return _config_vars
+
+    if re.search('-arch\s+ppc', _config_vars['CFLAGS']) is not None:
+        # NOTE: Cannot use subprocess here because of bootstrap
+        # issues when building Python itself
+        status = os.system("'%s' -arch ppc -x c /dev/null 2>/dev/null"%(
+            _config_vars['CC'].replace("'", "'\"'\"'"),))
+        # The Apple compiler drivers return status 255 if no PPC
+        if (status >> 8) == 255:
+            # Compiler doesn't support PPC, remove the related
+            # '-arch' flags if not explicitly overridden by an
+            # environment variable
+            for cv in _UNIVERSAL_CONFIG_VARS:
+                if cv in _config_vars and cv not in os.environ:
+                    flags = _config_vars[cv]
+                    flags = re.sub('-arch\s+ppc\w*\s', ' ', flags)
+                    _save_modified_value(_config_vars, cv, flags)
+
+    return _config_vars
+
+
+def _override_all_archs(_config_vars):
+    """Allow override of all archs with ARCHFLAGS env var"""
+    # 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 cv in _UNIVERSAL_CONFIG_VARS:
+            if cv in _config_vars and '-arch' in _config_vars[cv]:
+                flags = _config_vars[cv]
+                flags = re.sub('-arch\s+\w+\s', ' ', flags)
+                flags = flags + ' ' + arch
+                _save_modified_value(_config_vars, cv, flags)
+
+    return _config_vars
+
+
+def _check_for_unavailable_sdk(_config_vars):
+    """Remove references to any SDKs not available"""
+    # If we're on OSX 10.5 or later and the user tries to
+    # compile an extension using an SDK that is not present
+    # on the current machine it is better to not use an SDK
+    # than to fail.  This is particularly important with
+    # the standalong Command Line Tools alternative to a
+    # full-blown Xcode install since the CLT packages do not
+    # provide SDKs.  If the SDK is not present, it is assumed
+    # that the header files and dev libs have been installed
+    # to /usr and /System/Library by either a standalone CLT
+    # package or the CLT component within Xcode.
+    cflags = _config_vars.get('CFLAGS', '')
+    m = re.search(r'-isysroot\s+(\S+)', cflags)
+    if m is not None:
+        sdk = m.group(1)
+        if not os.path.exists(sdk):
+            for cv in _UNIVERSAL_CONFIG_VARS:
+                # Do not alter a config var explicitly overriden by env var
+                if cv in _config_vars and cv not in os.environ:
+                    flags = _config_vars[cv]
+                    flags = re.sub(r'-isysroot\s+\S+(?:\s|$)', ' ', flags)
+                    _save_modified_value(_config_vars, cv, flags)
+
+    return _config_vars
+
+
+def compiler_fixup(compiler_so, cc_args):
+    """
+    This function will strip '-isysroot PATH' and '-arch ARCH' from the
+    compile flags if the user has specified one them in extra_compile_flags.
+
+    This is needed because '-arch ARCH' adds another architecture to the
+    build, without a way to remove an architecture. Furthermore GCC will
+    barf if multiple '-isysroot' arguments are present.
+    """
+    stripArch = stripSysroot = False
+
+    compiler_so = list(compiler_so)
+
+    if not _supports_universal_builds():
+        # OSX before 10.4.0, these don't support -arch and -isysroot at
+        # all.
+        stripArch = stripSysroot = True
+    else:
+        stripArch = '-arch' in cc_args
+        stripSysroot = '-isysroot' in cc_args
+
+    if stripArch or 'ARCHFLAGS' in os.environ:
+        while True:
+            try:
+                index = compiler_so.index('-arch')
+                # Strip this argument and the next one:
+                del compiler_so[index:index+2]
+            except ValueError:
+                break
+
+    if 'ARCHFLAGS' in os.environ and not stripArch:
+        # User specified different -arch flags in the environ,
+        # see also distutils.sysconfig
+        compiler_so = compiler_so + os.environ['ARCHFLAGS'].split()
+
+    if stripSysroot:
+        while True:
+            try:
+                index = compiler_so.index('-isysroot')
+                # Strip this argument and the next one:
+                del compiler_so[index:index+2]
+            except ValueError:
+                break
+
+    # Check if the SDK that is used during compilation actually exists,
+    # the universal build requires the usage of a universal SDK and not all
+    # users have that installed by default.
+    sysroot = None
+    if '-isysroot' in cc_args:
+        idx = cc_args.index('-isysroot')
+        sysroot = cc_args[idx+1]
+    elif '-isysroot' in compiler_so:
+        idx = compiler_so.index('-isysroot')
+        sysroot = compiler_so[idx+1]
+
+    if sysroot and not os.path.isdir(sysroot):
+        from distutils import log
+        log.warn("Compiling with an SDK that doesn't seem to exist: %s",
+                sysroot)
+        log.warn("Please check your Xcode installation")
+
+    return compiler_so
+
+
+def customize_config_vars(_config_vars):
+    """Customize Python build configuration variables.
+
+    Called internally from sysconfig with a mutable mapping
+    containing name/value pairs parsed from the configured
+    makefile used to build this interpreter.  Returns
+    the mapping updated as needed to reflect the environment
+    in which the interpreter is running; in the case of
+    a Python from a binary installer, the installed
+    environment may be very different from the build
+    environment, i.e. different OS levels, different
+    built tools, different available CPU architectures.
+
+    This customization is performed whenever
+    distutils.sysconfig.get_config_vars() is first
+    called.  It may be used in environments where no
+    compilers are present, i.e. when installing pure
+    Python dists.  Customization of compiler paths
+    and detection of unavailable archs is deferred
+    until the first extention module build is
+    requested (in distutils.sysconfig.customize_compiler).
+
+    Currently called from distutils.sysconfig
+    """
+
+    if not _supports_universal_builds():
+        # 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.
+        _remove_universal_flags(_config_vars)
+
+    # Allow user to override all archs with ARCHFLAGS env var
+    _override_all_archs(_config_vars)
+
+    # Remove references to sdks that are not found
+    _check_for_unavailable_sdk(_config_vars)
+
+    return _config_vars
+
+
+def customize_compiler(_config_vars):
+    """Customize compiler path and configuration variables.
+
+    This customization is performed when the first
+    extension module build is requested
+    in distutils.sysconfig.customize_compiler).
+    """
+
+    # Find a compiler to use for extension module builds
+    _find_appropriate_compiler(_config_vars)
+
+    # Remove ppc arch flags if not supported here
+    _remove_unsupported_archs(_config_vars)
+
+    # Allow user to override all archs with ARCHFLAGS env var
+    _override_all_archs(_config_vars)
+
+    return _config_vars
+
+
+def get_platform_osx(_config_vars, osname, release, machine):
+    """Filter values for get_platform()"""
+    # called from get_platform() in sysconfig and distutils.util
+    #
+    # 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.
+
+    macver = _config_vars.get('MACOSX_DEPLOYMENT_TARGET', '')
+    macrelease = _get_system_version() or macver
+    macver = macver or macrelease
+
+    if macver:
+        release = macver
+        osname = "macosx"
+
+        # Use the original CFLAGS value, if available, so that we
+        # return the same machine type for the platform string.
+        # Otherwise, distutils may consider this a cross-compiling
+        # case and disallow installs.
+        cflags = _config_vars.get(_INITPRE+'CFLAGS',
+                                    _config_vars.get('CFLAGS', ''))
+        if ((macrelease + '.') >= '10.4.' and
+            '-arch' in cflags.strip()):
+            # The universal build will build fat binaries, but not on
+            # systems before 10.4
+
+            machine = 'fat'
+
+            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.maxint >= 2**32:
+                machine = 'x86_64'
+
+        elif machine in ('PowerPC', 'Power_Macintosh'):
+            # Pick a sane name for the PPC architecture.
+            # See 'i386' case
+            if sys.maxint >= 2**32:
+                machine = 'ppc64'
+            else:
+                machine = 'ppc'
+
+    return (osname, release, machine)
diff --git a/lib-python/2.7/_pyio.py b/lib-python/2.7/_pyio.py
--- a/lib-python/2.7/_pyio.py
+++ b/lib-python/2.7/_pyio.py
@@ -340,8 +340,10 @@
         This method has no effect if the file is already closed.
         """
         if not self.__closed:
-            self.flush()
-            self.__closed = True
+            try:
+                self.flush()
+            finally:
+                self.__closed = True
 
     def __del__(self):
         """Destructor.  Calls close()."""
@@ -883,12 +885,18 @@
         return pos
 
     def readable(self):
+        if self.closed:
+            raise ValueError("I/O operation on closed file.")
         return True
 
     def writable(self):
+        if self.closed:
+            raise ValueError("I/O operation on closed file.")
         return True
 
     def seekable(self):
+        if self.closed:
+            raise ValueError("I/O operation on closed file.")
         return True
 
 
@@ -1546,6 +1554,8 @@
         return self._buffer
 
     def seekable(self):
+        if self.closed:
+            raise ValueError("I/O operation on closed file.")
         return self._seekable
 
     def readable(self):
@@ -1560,8 +1570,10 @@
 
     def close(self):
         if self.buffer is not None and not self.closed:
-            self.flush()
-            self.buffer.close()
+            try:
+                self.flush()
+            finally:
+                self.buffer.close()
 
     @property
     def closed(self):
diff --git a/lib-python/2.7/_strptime.py b/lib-python/2.7/_strptime.py
--- a/lib-python/2.7/_strptime.py
+++ b/lib-python/2.7/_strptime.py
@@ -326,7 +326,8 @@
     if len(data_string) != found.end():
         raise ValueError("unconverted data remains: %s" %
                           data_string[found.end():])
-    year = 1900
+
+    year = None
     month = day = 1
     hour = minute = second = fraction = 0
     tz = -1
@@ -425,6 +426,12 @@
                     else:
                         tz = value
                         break
+    leap_year_fix = False
+    if year is None and month == 2 and day == 29:
+        year = 1904  # 1904 is first leap year of 20th century
+        leap_year_fix = True
+    elif year is None:
+        year = 1900
     # If we know the week of the year and what day of that week, we can figure
     # out the Julian day of the year.
     if julian == -1 and week_of_year != -1 and weekday != -1:
@@ -446,6 +453,12 @@
         day = datetime_result.day
     if weekday == -1:
         weekday = datetime_date(year, month, day).weekday()
+    if leap_year_fix:
+        # the caller didn't supply a year but asked for Feb 29th. We couldn't
+        # use the default of 1900 for computations. We set it back to ensure
+        # that February 29th is smaller than March 1st.
+        year = 1900
+
     return (time.struct_time((year, month, day,
                               hour, minute, second,
                               weekday, julian, tz)), fraction)
diff --git a/lib-python/2.7/aifc.py b/lib-python/2.7/aifc.py
--- a/lib-python/2.7/aifc.py
+++ b/lib-python/2.7/aifc.py
@@ -732,22 +732,28 @@
             self._patchheader()
 
     def close(self):
-        self._ensure_header_written(0)
-        if self._datawritten & 1:
-            # quick pad to even size
-            self._file.write(chr(0))
-            self._datawritten = self._datawritten + 1
-        self._writemarkers()
-        if self._nframeswritten != self._nframes or \
-              self._datalength != self._datawritten or \
-              self._marklength:
-            self._patchheader()
-        if self._comp:
-            self._comp.CloseCompressor()
-            self._comp = None
-        # Prevent ref cycles
-        self._convert = None
-        self._file.close()
+        if self._file is None:
+            return
+        try:
+            self._ensure_header_written(0)
+            if self._datawritten & 1:
+                # quick pad to even size
+                self._file.write(chr(0))
+                self._datawritten = self._datawritten + 1
+            self._writemarkers()
+            if self._nframeswritten != self._nframes or \
+                  self._datalength != self._datawritten or \
+                  self._marklength:
+                self._patchheader()
+            if self._comp:
+                self._comp.CloseCompressor()
+                self._comp = None
+        finally:
+            # Prevent ref cycles
+            self._convert = None
+            f = self._file
+            self._file = None
+            f.close()
 
     #
     # Internal methods.
diff --git a/lib-python/2.7/argparse.py b/lib-python/2.7/argparse.py
--- a/lib-python/2.7/argparse.py
+++ b/lib-python/2.7/argparse.py
@@ -740,10 +740,10 @@
 
         - default -- The value to be produced if the option is not specified.
 
-        - type -- The type which the command-line arguments should be converted
-            to, should be one of 'string', 'int', 'float', 'complex' or a
-            callable object that accepts a single string argument. If None,
-            'string' is assumed.
+        - type -- A callable that accepts a single string argument, and
+            returns the converted value.  The standard Python types str, int,
+            float, and complex are useful examples of such callables.  If None,
+            str is used.
 
         - choices -- A container of values that should be allowed. If not None,
             after a command-line argument has been converted to the appropriate
@@ -1692,9 +1692,12 @@
         return args
 
     def parse_known_args(self, args=None, namespace=None):
-        # args default to the system args
         if args is None:
+            # args default to the system args
             args = _sys.argv[1:]
+        else:
+            # make sure that args are mutable
+            args = list(args)
 
         # default Namespace built from parser defaults
         if namespace is None:
@@ -1705,10 +1708,7 @@
             if action.dest is not SUPPRESS:
                 if not hasattr(namespace, action.dest):
                     if action.default is not SUPPRESS:
-                        default = action.default
-                        if isinstance(action.default, basestring):
-                            default = self._get_value(action, default)
-                        setattr(namespace, action.dest, default)
+                        setattr(namespace, action.dest, action.default)
 
         # add any parser defaults that aren't present
         for dest in self._defaults:
@@ -1936,12 +1936,23 @@
         if positionals:
             self.error(_('too few arguments'))
 
-        # make sure all required actions were present
+        # make sure all required actions were present, and convert defaults.
         for action in self._actions:
-            if action.required:
-                if action not in seen_actions:
+            if action not in seen_actions:
+                if action.required:
                     name = _get_action_name(action)
                     self.error(_('argument %s is required') % name)
+                else:
+                    # Convert action default now instead of doing it before
+                    # parsing arguments to avoid calling convert functions
+                    # twice (which may fail) if the argument was given, but
+                    # only if it was defined already in the namespace
+                    if (action.default is not None and
+                            isinstance(action.default, basestring) and
+                            hasattr(namespace, action.dest) and
+                            action.default is getattr(namespace, action.dest)):
+                        setattr(namespace, action.dest,
+                                self._get_value(action, action.default))
 
         # make sure all required groups had one option present
         for group in self._mutually_exclusive_groups:
@@ -1967,7 +1978,7 @@
         for arg_string in arg_strings:
 
             # for regular arguments, just add them back into the list
-            if arg_string[0] not in self.fromfile_prefix_chars:
+            if not arg_string or arg_string[0] not in self.fromfile_prefix_chars:
                 new_arg_strings.append(arg_string)
 
             # replace arguments referencing files with the file content
@@ -2174,9 +2185,12 @@
     # Value conversion methods
     # ========================
     def _get_values(self, action, arg_strings):
-        # for everything but PARSER args, strip out '--'
+        # for everything but PARSER, REMAINDER args, strip out first '--'
         if action.nargs not in [PARSER, REMAINDER]:
-            arg_strings = [s for s in arg_strings if s != '--']
+            try:
+                arg_strings.remove('--')
+            except ValueError:
+                pass
 
         # optional argument produces a default when not present
         if not arg_strings and action.nargs == OPTIONAL:
diff --git a/lib-python/2.7/asyncore.py b/lib-python/2.7/asyncore.py
--- a/lib-python/2.7/asyncore.py
+++ b/lib-python/2.7/asyncore.py
@@ -225,6 +225,7 @@
     debug = False
     connected = False
     accepting = False
+    connecting = False
     closing = False
     addr = None
     ignore_log_types = frozenset(['warning'])
@@ -248,7 +249,7 @@
             try:
                 self.addr = sock.getpeername()
             except socket.error, err:
-                if err.args[0] == ENOTCONN:
+                if err.args[0] in (ENOTCONN, EINVAL):
                     # To handle the case where we got an unconnected
                     # socket.
                     self.connected = False
@@ -342,9 +343,11 @@
 
     def connect(self, address):
         self.connected = False
+        self.connecting = True
         err = self.socket.connect_ex(address)
         if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \
         or err == EINVAL and os.name in ('nt', 'ce'):
+            self.addr = address
             return
         if err in (0, EISCONN):
             self.addr = address
@@ -390,7 +393,7 @@
             else:
                 return data
         except socket.error, why:
-            # winsock sometimes throws ENOTCONN
+            # winsock sometimes raises ENOTCONN
             if why.args[0] in _DISCONNECTED:
                 self.handle_close()
                 return ''
@@ -400,6 +403,7 @@
     def close(self):
         self.connected = False
         self.accepting = False
+        self.connecting = False
         self.del_channel()
         try:
             self.socket.close()
@@ -438,7 +442,8 @@
             # sockets that are connected
             self.handle_accept()
         elif not self.connected:
-            self.handle_connect_event()
+            if self.connecting:
+                self.handle_connect_event()
             self.handle_read()
         else:
             self.handle_read()
@@ -449,6 +454,7 @@
             raise socket.error(err, _strerror(err))
         self.handle_connect()
         self.connected = True
+        self.connecting = False
 
     def handle_write_event(self):
         if self.accepting:
@@ -457,12 +463,8 @@
             return
 
         if not self.connected:
-            #check for errors
-            err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
-            if err != 0:
-                raise socket.error(err, _strerror(err))
-
-            self.handle_connect_event()
+            if self.connecting:
+                self.handle_connect_event()
         self.handle_write()
 
     def handle_expt_event(self):
diff --git a/lib-python/2.7/bdb.py b/lib-python/2.7/bdb.py
--- a/lib-python/2.7/bdb.py
+++ b/lib-python/2.7/bdb.py
@@ -24,6 +24,7 @@
         self.skip = set(skip) if skip else None
         self.breaks = {}
         self.fncache = {}
+        self.frame_returning = None
 
     def canonic(self, filename):
         if filename == "<" + filename[1:-1] + ">":
@@ -82,7 +83,11 @@
 
     def dispatch_return(self, frame, arg):
         if self.stop_here(frame) or frame == self.returnframe:
-            self.user_return(frame, arg)
+            try:
+                self.frame_returning = frame
+                self.user_return(frame, arg)
+            finally:
+                self.frame_returning = None
             if self.quitting: raise BdbQuit
         return self.trace_dispatch
 
@@ -186,6 +191,14 @@
 
     def set_step(self):
         """Stop after one line of code."""
+        # Issue #13183: pdb skips frames after hitting a breakpoint and running
+        # step commands.
+        # Restore the trace function in the caller (that may not have been set
+        # for performance reasons) when returning from the current frame.
+        if self.frame_returning:
+            caller_frame = self.frame_returning.f_back
+            if caller_frame and not caller_frame.f_trace:
+                caller_frame.f_trace = self.trace_dispatch
         self._set_stopinfo(None, None)
 
     def set_next(self, frame):
diff --git a/lib-python/2.7/calendar.py b/lib-python/2.7/calendar.py
--- a/lib-python/2.7/calendar.py
+++ b/lib-python/2.7/calendar.py
@@ -161,7 +161,11 @@
         oneday = datetime.timedelta(days=1)
         while True:
             yield date
-            date += oneday
+            try:
+                date += oneday
+            except OverflowError:
+                # Adding one day could fail after datetime.MAXYEAR
+                break
             if date.month != month and date.weekday() == self.firstweekday:
                 break
 
@@ -488,6 +492,7 @@
     def __enter__(self):
         self.oldlocale = _locale.getlocale(_locale.LC_TIME)
         _locale.setlocale(_locale.LC_TIME, self.locale)
+        return _locale.getlocale(_locale.LC_TIME)[1]
 
     def __exit__(self, *args):
         _locale.setlocale(_locale.LC_TIME, self.oldlocale)
diff --git a/lib-python/2.7/cgi.py b/lib-python/2.7/cgi.py
--- a/lib-python/2.7/cgi.py
+++ b/lib-python/2.7/cgi.py
@@ -37,7 +37,6 @@
 from operator import attrgetter
 import sys
 import os
-import urllib
 import UserDict
 import urlparse
 
diff --git a/lib-python/2.7/cgitb.py b/lib-python/2.7/cgitb.py
--- a/lib-python/2.7/cgitb.py
+++ b/lib-python/2.7/cgitb.py
@@ -295,14 +295,19 @@
         if self.logdir is not None:
             suffix = ['.txt', '.html'][self.format=="html"]
             (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir)
+
             try:
                 file = os.fdopen(fd, 'w')
                 file.write(doc)
                 file.close()
-                msg = '<p> %s contains the description of this error.' % path
+                msg = '%s contains the description of this error.' % path
             except:
-                msg = '<p> Tried to save traceback to %s, but failed.' % path
-            self.file.write(msg + '\n')
+                msg = 'Tried to save traceback to %s, but failed.' % path
+
+            if self.format == 'html':
+                self.file.write('<p>%s</p>\n' % msg)
+            else:
+                self.file.write(msg + '\n')
         try:
             self.file.flush()
         except: pass
diff --git a/lib-python/2.7/cmd.py b/lib-python/2.7/cmd.py
--- a/lib-python/2.7/cmd.py
+++ b/lib-python/2.7/cmd.py
@@ -294,6 +294,7 @@
         return list(commands | topics)
 
     def do_help(self, arg):
+        'List available commands with "help" or detailed help with "help cmd".'
         if arg:
             # XXX check arg syntax
             try:
diff --git a/lib-python/2.7/collections.py b/lib-python/2.7/collections.py
--- a/lib-python/2.7/collections.py
+++ b/lib-python/2.7/collections.py
@@ -6,11 +6,12 @@
 __all__ += _abcoll.__all__
 
 from _collections import deque, defaultdict
-from operator import itemgetter as _itemgetter
+from operator import itemgetter as _itemgetter, eq as _eq
 from keyword import iskeyword as _iskeyword
 import sys as _sys
 import heapq as _heapq
 from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
+from itertools import imap as _imap
 
 try:
     from thread import get_ident as _get_ident
@@ -50,49 +51,45 @@
             self.__map = {}
         self.__update(*args, **kwds)
 
-    def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__):
+    def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
         'od.__setitem__(i, y) <==> od[i]=y'
         # Setting a new item creates a new link at the end of the linked list,
         # and the inherited dictionary is updated with the new key/value pair.
         if key not in self:
             root = self.__root
-            last = root[PREV]
-            last[NEXT] = root[PREV] = self.__map[key] = [last, root, key]
-        dict_setitem(self, key, value)
+            last = root[0]
+            last[1] = root[0] = self.__map[key] = [last, root, key]
+        return dict_setitem(self, key, value)
 
-    def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__):
+    def __delitem__(self, key, dict_delitem=dict.__delitem__):
         'od.__delitem__(y) <==> del od[y]'
         # Deleting an existing item uses self.__map to find the link which gets
         # removed by updating the links in the predecessor and successor nodes.
         dict_delitem(self, key)
         link_prev, link_next, key = self.__map.pop(key)
-        link_prev[NEXT] = link_next
-        link_next[PREV] = link_prev
+        link_prev[1] = link_next                        # update link_prev[NEXT]
+        link_next[0] = link_prev                        # update link_next[PREV]
 
     def __iter__(self):
         'od.__iter__() <==> iter(od)'
         # Traverse the linked list in order.
-        NEXT, KEY = 1, 2
         root = self.__root
-        curr = root[NEXT]
+        curr = root[1]                                  # start at the first node
         while curr is not root:
-            yield curr[KEY]
-            curr = curr[NEXT]
+            yield curr[2]                               # yield the curr[KEY]
+            curr = curr[1]                              # move to next node
 
     def __reversed__(self):
         'od.__reversed__() <==> reversed(od)'
         # Traverse the linked list in reverse order.
-        PREV, KEY = 0, 2
         root = self.__root
-        curr = root[PREV]
+        curr = root[0]                                  # start at the last node
         while curr is not root:
-            yield curr[KEY]
-            curr = curr[PREV]
+            yield curr[2]                               # yield the curr[KEY]
+            curr = curr[0]                              # move to previous node
 
     def clear(self):
         'od.clear() -> None.  Remove all items from od.'
-        for node in self.__map.itervalues():
-            del node[:]
         root = self.__root
         root[:] = [root, root, None]
         self.__map.clear()
@@ -208,7 +205,7 @@
 
         '''
         if isinstance(other, OrderedDict):
-            return len(self)==len(other) and self.items() == other.items()
+            return dict.__eq__(self, other) and all(_imap(_eq, self, other))
         return dict.__eq__(self, other)
 
     def __ne__(self, other):
@@ -234,10 +231,60 @@
 ### namedtuple
 ################################################################################
 
+_class_template = '''\
+class {typename}(tuple):
+    '{typename}({arg_list})'
+
+    __slots__ = ()
+
+    _fields = {field_names!r}
+
+    def __new__(_cls, {arg_list}):
+        'Create new instance of {typename}({arg_list})'
+        return _tuple.__new__(_cls, ({arg_list}))
+
+    @classmethod
+    def _make(cls, iterable, new=tuple.__new__, len=len):
+        'Make a new {typename} object from a sequence or iterable'
+        result = new(cls, iterable)
+        if len(result) != {num_fields:d}:
+            raise TypeError('Expected {num_fields:d} arguments, got %d' % len(result))
+        return result
+
+    def __repr__(self):
+        'Return a nicely formatted representation string'
+        return '{typename}({repr_fmt})' % self
+
+    def _asdict(self):
+        'Return a new OrderedDict which maps field names to their values'
+        return OrderedDict(zip(self._fields, self))
+
+    __dict__ = property(_asdict)
+
+    def _replace(_self, **kwds):
+        'Return a new {typename} object replacing specified fields with new values'
+        result = _self._make(map(kwds.pop, {field_names!r}, _self))
+        if kwds:
+            raise ValueError('Got unexpected field names: %r' % kwds.keys())
+        return result
+
+    def __getnewargs__(self):
+        'Return self as a plain tuple.  Used by copy and pickle.'
+        return tuple(self)
+
+{field_defs}
+'''
+
+_repr_template = '{name}=%r'
+
+_field_template = '''\
+    {name} = _property(_itemgetter({index:d}), doc='Alias for field number {index:d}')
+'''
+
 def namedtuple(typename, field_names, verbose=False, rename=False):
     """Returns a new subclass of tuple with named fields.
 
-    >>> Point = namedtuple('Point', 'x y')
+    >>> Point = namedtuple('Point', ['x', 'y'])
     >>> Point.__doc__                   # docstring for the new class
     'Point(x, y)'
     >>> p = Point(11, y=22)             # instantiate with positional args or keywords
@@ -258,83 +305,63 @@
 
     """
 
-    # Parse and validate the field names.  Validation serves two purposes,
-    # generating informative error messages and preventing template injection attacks.
+    # Validate the field names.  At the user's option, either generate an error
+    # message or automatically replace the field name with a valid name.
     if isinstance(field_names, basestring):
-        field_names = field_names.replace(',', ' ').split() # names separated by whitespace and/or commas
-    field_names = tuple(map(str, field_names))
+        field_names = field_names.replace(',', ' ').split()
+    field_names = map(str, field_names)
     if rename:
-        names = list(field_names)
         seen = set()
-        for i, name in enumerate(names):
-            if (not all(c.isalnum() or c=='_' for c in name) or _iskeyword(name)
-                or not name or name[0].isdigit() or name.startswith('_')
+        for index, name in enumerate(field_names):
+            if (not all(c.isalnum() or c=='_' for c in name)
+                or _iskeyword(name)
+                or not name
+                or name[0].isdigit()
+                or name.startswith('_')
                 or name in seen):
-                names[i] = '_%d' % i
+                field_names[index] = '_%d' % index
             seen.add(name)
-        field_names = tuple(names)
-    for name in (typename,) + field_names:
+    for name in [typename] + field_names:
         if not all(c.isalnum() or c=='_' for c in name):
-            raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
+            raise ValueError('Type names and field names can only contain '
+                             'alphanumeric characters and underscores: %r' % name)
         if _iskeyword(name):
-            raise ValueError('Type names and field names cannot be a keyword: %r' % name)
+            raise ValueError('Type names and field names cannot be a '
+                             'keyword: %r' % name)
         if name[0].isdigit():
-            raise ValueError('Type names and field names cannot start with a number: %r' % name)
-    seen_names = set()
+            raise ValueError('Type names and field names cannot start with '
+                             'a number: %r' % name)
+    seen = set()
     for name in field_names:
         if name.startswith('_') and not rename:
-            raise ValueError('Field names cannot start with an underscore: %r' % name)
-        if name in seen_names:
+            raise ValueError('Field names cannot start with an underscore: '
+                             '%r' % name)
+        if name in seen:
             raise ValueError('Encountered duplicate field name: %r' % name)
-        seen_names.add(name)
+        seen.add(name)
 
-    # Create and fill-in the class template
-    numfields = len(field_names)
-    argtxt = repr(field_names).replace("'", "")[1:-1]   # tuple repr without parens or quotes
-    reprtxt = ', '.join('%s=%%r' % name for name in field_names)
-    template = '''class %(typename)s(tuple):
-        '%(typename)s(%(argtxt)s)' \n
-        __slots__ = () \n
-        _fields = %(field_names)r \n
-        def __new__(_cls, %(argtxt)s):
-            'Create new instance of %(typename)s(%(argtxt)s)'
-            return _tuple.__new__(_cls, (%(argtxt)s)) \n
-        @classmethod
-        def _make(cls, iterable, new=tuple.__new__, len=len):
-            'Make a new %(typename)s object from a sequence or iterable'
-            result = new(cls, iterable)
-            if len(result) != %(numfields)d:
-                raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result))
-            return result \n
-        def __repr__(self):
-            'Return a nicely formatted representation string'
-            return '%(typename)s(%(reprtxt)s)' %% self \n
-        def _asdict(self):
-            'Return a new OrderedDict which maps field names to their values'
-            return OrderedDict(zip(self._fields, self)) \n
-        __dict__ = property(_asdict) \n
-        def _replace(_self, **kwds):
-            'Return a new %(typename)s object replacing specified fields with new values'
-            result = _self._make(map(kwds.pop, %(field_names)r, _self))
-            if kwds:
-                raise ValueError('Got unexpected field names: %%r' %% kwds.keys())
-            return result \n
-        def __getnewargs__(self):
-            'Return self as a plain tuple.  Used by copy and pickle.'
-            return tuple(self) \n\n''' % locals()
-    for i, name in enumerate(field_names):
-        template += "        %s = _property(_itemgetter(%d), doc='Alias for field number %d')\n" % (name, i, i)
+    # Fill-in the class template
+    class_definition = _class_template.format(
+        typename = typename,
+        field_names = tuple(field_names),
+        num_fields = len(field_names),
+        arg_list = repr(tuple(field_names)).replace("'", "")[1:-1],
+        repr_fmt = ', '.join(_repr_template.format(name=name)
+                             for name in field_names),
+        field_defs = '\n'.join(_field_template.format(index=index, name=name)
+                               for index, name in enumerate(field_names))
+    )
     if verbose:
-        print template
+        print class_definition
 
-    # Execute the template string in a temporary namespace and
-    # support tracing utilities by setting a value for frame.f_globals['__name__']
+    # Execute the template string in a temporary namespace and support
+    # tracing utilities by setting a value for frame.f_globals['__name__']
     namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
                      OrderedDict=OrderedDict, _property=property, _tuple=tuple)
     try:
-        exec template in namespace
-    except SyntaxError, e:
-        raise SyntaxError(e.message + ':\n' + template)
+        exec class_definition in namespace
+    except SyntaxError as e:
+        raise SyntaxError(e.message + ':\n' + class_definition)
     result = namespace[typename]
 
     # For pickling to work, the __module__ variable needs to be set to the frame
diff --git a/lib-python/2.7/compiler/consts.py b/lib-python/2.7/compiler/consts.py
--- a/lib-python/2.7/compiler/consts.py
+++ b/lib-python/2.7/compiler/consts.py
@@ -5,7 +5,7 @@
 
 SC_LOCAL = 1
 SC_GLOBAL_IMPLICIT = 2
-SC_GLOBAL_EXPLICT = 3
+SC_GLOBAL_EXPLICIT = 3
 SC_FREE = 4
 SC_CELL = 5
 SC_UNKNOWN = 6
diff --git a/lib-python/2.7/compiler/pycodegen.py b/lib-python/2.7/compiler/pycodegen.py
--- a/lib-python/2.7/compiler/pycodegen.py
+++ b/lib-python/2.7/compiler/pycodegen.py
@@ -7,7 +7,7 @@
 
 from compiler import ast, parse, walk, syntax
 from compiler import pyassem, misc, future, symbols
-from compiler.consts import SC_LOCAL, SC_GLOBAL_IMPLICIT, SC_GLOBAL_EXPLICT, \
+from compiler.consts import SC_LOCAL, SC_GLOBAL_IMPLICIT, SC_GLOBAL_EXPLICIT, \
      SC_FREE, SC_CELL
 from compiler.consts import (CO_VARARGS, CO_VARKEYWORDS, CO_NEWLOCALS,
      CO_NESTED, CO_GENERATOR, CO_FUTURE_DIVISION,
@@ -283,7 +283,7 @@
                 self.emit(prefix + '_NAME', name)
             else:
                 self.emit(prefix + '_FAST', name)
-        elif scope == SC_GLOBAL_EXPLICT:
+        elif scope == SC_GLOBAL_EXPLICIT:
             self.emit(prefix + '_GLOBAL', name)
         elif scope == SC_GLOBAL_IMPLICIT:
             if not self.optimized:
diff --git a/lib-python/2.7/compiler/symbols.py b/lib-python/2.7/compiler/symbols.py
--- a/lib-python/2.7/compiler/symbols.py
+++ b/lib-python/2.7/compiler/symbols.py
@@ -1,7 +1,7 @@
 """Module symbol-table generator"""
 
 from compiler import ast
-from compiler.consts import SC_LOCAL, SC_GLOBAL_IMPLICIT, SC_GLOBAL_EXPLICT, \
+from compiler.consts import SC_LOCAL, SC_GLOBAL_IMPLICIT, SC_GLOBAL_EXPLICIT, \
     SC_FREE, SC_CELL, SC_UNKNOWN
 from compiler.misc import mangle
 import types
@@ -90,7 +90,7 @@
         The scope of a name could be LOCAL, GLOBAL, FREE, or CELL.
         """
         if name in self.globals:
-            return SC_GLOBAL_EXPLICT
+            return SC_GLOBAL_EXPLICIT
         if name in self.cells:
             return SC_CELL
         if name in self.defs:
diff --git a/lib-python/2.7/ctypes/test/test_bitfields.py b/lib-python/2.7/ctypes/test/test_bitfields.py
--- a/lib-python/2.7/ctypes/test/test_bitfields.py
+++ b/lib-python/2.7/ctypes/test/test_bitfields.py
@@ -240,5 +240,25 @@
             _anonymous_ = ["_"]
             _fields_ = [("_", X)]
 
+    @unittest.skipUnless(hasattr(ctypes, "c_uint32"), "c_int32 is required")
+    def test_uint32(self):
+        class X(Structure):
+            _fields_ = [("a", c_uint32, 32)]
+        x = X()
+        x.a = 10
+        self.assertEqual(x.a, 10)
+        x.a = 0xFDCBA987
+        self.assertEqual(x.a, 0xFDCBA987)
+
+    @unittest.skipUnless(hasattr(ctypes, "c_uint64"), "c_int64 is required")
+    def test_uint64(self):
+        class X(Structure):
+            _fields_ = [("a", c_uint64, 64)]
+        x = X()
+        x.a = 10
+        self.assertEqual(x.a, 10)
+        x.a = 0xFEDCBA9876543211
+        self.assertEqual(x.a, 0xFEDCBA9876543211)
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/lib-python/2.7/ctypes/test/test_numbers.py b/lib-python/2.7/ctypes/test/test_numbers.py
--- a/lib-python/2.7/ctypes/test/test_numbers.py
+++ b/lib-python/2.7/ctypes/test/test_numbers.py
@@ -216,6 +216,16 @@
         # probably be changed:
         self.assertRaises(TypeError, c_int, c_long(42))
 
+    def test_float_overflow(self):
+        import sys
+        big_int = int(sys.float_info.max) * 2
+        for t in float_types + [c_longdouble]:
+            self.assertRaises(OverflowError, t, big_int)
+            if (hasattr(t, "__ctype_be__")):
+                self.assertRaises(OverflowError, t.__ctype_be__, big_int)
+            if (hasattr(t, "__ctype_le__")):
+                self.assertRaises(OverflowError, t.__ctype_le__, big_int)
+
 ##    def test_perf(self):
 ##        check_perf()
 
diff --git a/lib-python/2.7/ctypes/test/test_returnfuncptrs.py b/lib-python/2.7/ctypes/test/test_returnfuncptrs.py
--- a/lib-python/2.7/ctypes/test/test_returnfuncptrs.py
+++ b/lib-python/2.7/ctypes/test/test_returnfuncptrs.py
@@ -1,5 +1,6 @@
 import unittest
 from ctypes import *
+import os
 
 import _ctypes_test
 
@@ -31,5 +32,34 @@
         self.assertRaises(ArgumentError, strchr, "abcdef", 3)
         self.assertRaises(TypeError, strchr, "abcdef")
 
+    def test_from_dll(self):
+        dll = CDLL(_ctypes_test.__file__)
+        # _CFuncPtr instances are now callable with a tuple argument
+        # which denotes a function name and a dll:
+        strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(("my_strchr", dll))
+        self.assertTrue(strchr(b"abcdef", b"b"), "bcdef")
+        self.assertEqual(strchr(b"abcdef", b"x"), None)
+        self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0)
+        self.assertRaises(TypeError, strchr, b"abcdef")
+
+    # Issue 6083: Reference counting bug
+    def test_from_dll_refcount(self):
+        class BadSequence(tuple):
+            def __getitem__(self, key):
+                if key == 0:
+                    return "my_strchr"
+                if key == 1:
+                    return CDLL(_ctypes_test.__file__)
+                raise IndexError
+
+        # _CFuncPtr instances are now callable with a tuple argument
+        # which denotes a function name and a dll:
+        strchr = CFUNCTYPE(c_char_p, c_char_p, c_char)(
+                BadSequence(("my_strchr", CDLL(_ctypes_test.__file__))))
+        self.assertTrue(strchr(b"abcdef", b"b"), "bcdef")
+        self.assertEqual(strchr(b"abcdef", b"x"), None)
+        self.assertRaises(ArgumentError, strchr, b"abcdef", 3.0)
+        self.assertRaises(TypeError, strchr, b"abcdef")
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/lib-python/2.7/ctypes/test/test_structures.py b/lib-python/2.7/ctypes/test/test_structures.py
--- a/lib-python/2.7/ctypes/test/test_structures.py
+++ b/lib-python/2.7/ctypes/test/test_structures.py
@@ -1,6 +1,7 @@
 import unittest
 from ctypes import *
 from struct import calcsize
+import _testcapi
 
 class SubclassesTest(unittest.TestCase):
     def test_subclass(self):
@@ -199,6 +200,14 @@
              "_pack_": -1}
         self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
 
+        # Issue 15989
+        d = {"_fields_": [("a", c_byte)],
+             "_pack_": _testcapi.INT_MAX + 1}
+        self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
+        d = {"_fields_": [("a", c_byte)],
+             "_pack_": _testcapi.UINT_MAX + 2}
+        self.assertRaises(ValueError, type(Structure), "X", (Structure,), d)
+
     def test_initializers(self):
         class Person(Structure):
             _fields_ = [("name", c_char*6),
diff --git a/lib-python/2.7/ctypes/test/test_win32.py b/lib-python/2.7/ctypes/test/test_win32.py
--- a/lib-python/2.7/ctypes/test/test_win32.py
+++ b/lib-python/2.7/ctypes/test/test_win32.py
@@ -3,6 +3,7 @@
 from ctypes import *
 from ctypes.test import is_resource_enabled
 import unittest, sys
+from test import test_support as support
 
 import _ctypes_test
 
@@ -60,7 +61,9 @@
 
         def test_COMError(self):
             from _ctypes import COMError
-            self.assertEqual(COMError.__doc__, "Raised when a COM method call failed.")
+            if support.HAVE_DOCSTRINGS:
+                self.assertEqual(COMError.__doc__,
+                                 "Raised when a COM method call failed.")
 
             ex = COMError(-1, "text", ("details",))
             self.assertEqual(ex.hresult, -1)
diff --git a/lib-python/2.7/ctypes/util.py b/lib-python/2.7/ctypes/util.py
--- a/lib-python/2.7/ctypes/util.py
+++ b/lib-python/2.7/ctypes/util.py
@@ -180,6 +180,35 @@
             res.sort(cmp= lambda x,y: cmp(_num_version(x), _num_version(y)))
             return res[-1]
 
+    elif sys.platform == "sunos5":
+
+        def _findLib_crle(name, is64):
+            if not os.path.exists('/usr/bin/crle'):
+                return None
+
+            if is64:
+                cmd = 'env LC_ALL=C /usr/bin/crle -64 2>/dev/null'
+            else:
+                cmd = 'env LC_ALL=C /usr/bin/crle 2>/dev/null'
+
+            for line in os.popen(cmd).readlines():
+                line = line.strip()
+                if line.startswith('Default Library Path (ELF):'):
+                    paths = line.split()[4]
+
+            if not paths:
+                return None
+
+            for dir in paths.split(":"):
+                libfile = os.path.join(dir, "lib%s.so" % name)
+                if os.path.exists(libfile):
+                    return libfile
+
+            return None
+
+        def find_library(name, is64 = False):
+            return _get_soname(_findLib_crle(name, is64) or _findLib_gcc(name))
+
     else:
 
         def _findSoname_ldconfig(name):
diff --git a/lib-python/2.7/curses/__init__.py b/lib-python/2.7/curses/__init__.py
--- a/lib-python/2.7/curses/__init__.py
+++ b/lib-python/2.7/curses/__init__.py
@@ -5,7 +5,7 @@
 
    import curses
    from curses import textpad
-   curses.initwin()
+   curses.initscr()
    ...
 
 """
diff --git a/lib-python/2.7/decimal.py b/lib-python/2.7/decimal.py
--- a/lib-python/2.7/decimal.py
+++ b/lib-python/2.7/decimal.py
@@ -1581,7 +1581,13 @@
 
     def __float__(self):
         """Float representation."""
-        return float(str(self))
+        if self._isnan():
+            if self.is_snan():
+                raise ValueError("Cannot convert signaling NaN to float")
+            s = "-nan" if self._sign else "nan"
+        else:
+            s = str(self)
+        return float(s)
 
     def __int__(self):
         """Converts self to an int, truncating if necessary."""
diff --git a/lib-python/2.7/distutils/__init__.py b/lib-python/2.7/distutils/__init__.py
--- a/lib-python/2.7/distutils/__init__.py
+++ b/lib-python/2.7/distutils/__init__.py
@@ -15,5 +15,5 @@
 # Updated automatically by the Python release process.
 #
 #--start constants--
-__version__ = "2.7.3rc2"
+__version__ = "2.7.3"
 #--end constants--
diff --git a/lib-python/2.7/distutils/ccompiler.py b/lib-python/2.7/distutils/ccompiler.py
--- a/lib-python/2.7/distutils/ccompiler.py
+++ b/lib-python/2.7/distutils/ccompiler.py
@@ -17,6 +17,8 @@
 from distutils.dep_util import newer_group
 from distutils.util import split_quoted, execute
 from distutils import log
+# following import is for backward compatibility
+from distutils.sysconfig import customize_compiler
 
 class CCompiler:
     """Abstract base class to define the interface that must be implemented
diff --git a/lib-python/2.7/distutils/command/check.py b/lib-python/2.7/distutils/command/check.py
--- a/lib-python/2.7/distutils/command/check.py
+++ b/lib-python/2.7/distutils/command/check.py
@@ -26,6 +26,9 @@
 
         def system_message(self, level, message, *children, **kwargs):
             self.messages.append((level, message, children, kwargs))
+            return nodes.system_message(message, level=level,
+                                        type=self.levels[level],
+                                        *children, **kwargs)
 
     HAS_DOCUTILS = True
 except ImportError:
diff --git a/lib-python/2.7/distutils/config.py b/lib-python/2.7/distutils/config.py
--- a/lib-python/2.7/distutils/config.py
+++ b/lib-python/2.7/distutils/config.py
@@ -42,16 +42,11 @@
     def _store_pypirc(self, username, password):
         """Creates a default .pypirc file."""
         rc = self._get_rc_file()
-        f = open(rc, 'w')
+        f = os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0600), 'w')
         try:
             f.write(DEFAULT_PYPIRC % (username, password))
         finally:
             f.close()
-        try:
-            os.chmod(rc, 0600)
-        except OSError:
-            # should do something better here
-            pass
 
     def _read_pypirc(self):
         """Reads the .pypirc file."""
diff --git a/lib-python/2.7/distutils/dir_util.py b/lib-python/2.7/distutils/dir_util.py
--- a/lib-python/2.7/distutils/dir_util.py
+++ b/lib-python/2.7/distutils/dir_util.py
@@ -144,6 +144,10 @@
         src_name = os.path.join(src, n)
         dst_name = os.path.join(dst, n)
 
+        if n.startswith('.nfs'):
+            # skip NFS rename files
+            continue
+
         if preserve_symlinks and os.path.islink(src_name):
             link_dest = os.readlink(src_name)
             if verbose >= 1:
diff --git a/lib-python/2.7/distutils/sysconfig.py b/lib-python/2.7/distutils/sysconfig.py
--- a/lib-python/2.7/distutils/sysconfig.py
+++ b/lib-python/2.7/distutils/sysconfig.py
@@ -37,6 +37,11 @@
     project_base = os.path.abspath(os.path.join(project_base, os.path.pardir,
                                                 os.path.pardir))
 
+# set for cross builds
+if "_PYTHON_PROJECT_BASE" in os.environ:
+    # this is the build directory, at least for posix
+    project_base = os.path.normpath(os.environ["_PYTHON_PROJECT_BASE"])
+
 # python_build: (Boolean) if true, we're either building Python or
 # building an extension with an un-installed Python, so we use
 # different (hard-wired) directories.
@@ -141,7 +146,7 @@
             "I don't know where Python installs its library "
             "on platform '%s'" % os.name)
 
-_USE_CLANG = None
+
 
 def customize_compiler(compiler):
     """Do any platform-specific customization of a CCompiler instance.
@@ -150,6 +155,21 @@
     varies across Unices and is stored in Python's Makefile.
     """
     if compiler.compiler_type == "unix":
+        if sys.platform == "darwin":
+            # Perform first-time customization of compiler-related
+            # config vars on OS X now that we know we need a compiler.
+            # This is primarily to support Pythons from binary
+            # installers.  The kind and paths to build tools on
+            # the user system may vary significantly from the system
+            # that Python itself was built on.  Also the user OS
+            # version and build tools may not support the same set
+            # of CPU architectures for universal builds.
+            global _config_vars
+            if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''):
+                import _osx_support
+                _osx_support.customize_compiler(_config_vars)
+                _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
+
         (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \
             get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS',
                             'CCSHARED', 'LDSHARED', 'SO', 'AR',
@@ -157,36 +177,7 @@
 
         newcc = None
         if 'CC' in os.environ:
-            newcc = os.environ['CC']
-        elif sys.platform == 'darwin' and cc == 'gcc-4.2':
-            # Issue #13590:
-            #       Since Apple removed gcc-4.2 in Xcode 4.2, we can no
-            #       longer assume it is available for extension module builds.
-            #       If Python was built with gcc-4.2, check first to see if
-            #       it is available on this system; if not, try to use clang
-            #       instead unless the caller explicitly set CC.
-            global _USE_CLANG
-            if _USE_CLANG is None:
-                from distutils import log
-                from subprocess import Popen, PIPE
-                p = Popen("! type gcc-4.2 && type clang && exit 2",
-                                shell=True, stdout=PIPE, stderr=PIPE)
-                p.wait()
-                if p.returncode == 2:
-                    _USE_CLANG = True
-                    log.warn("gcc-4.2 not found, using clang instead")
-                else:
-                    _USE_CLANG = False
-            if _USE_CLANG:
-                newcc = 'clang'
-        if newcc:
-            # On OS X, if CC is overridden, use that as the default
-            #       command for LDSHARED as well
-            if (sys.platform == 'darwin'
-                    and 'LDSHARED' not in os.environ
-                    and ldshared.startswith(cc)):
-                ldshared = newcc + ldshared[len(cc):]
-            cc = newcc
+            cc = os.environ['CC']
         if 'CXX' in os.environ:
             cxx = os.environ['CXX']
         if 'LDSHARED' in os.environ:
@@ -244,7 +235,7 @@
 def get_makefile_filename():
     """Return full pathname of installed Makefile from the Python build."""
     if python_build:
-        return os.path.join(os.path.dirname(sys.executable), "Makefile")
+        return os.path.join(project_base, "Makefile")
     lib_dir = get_python_lib(plat_specific=1, standard_lib=1)
     return os.path.join(lib_dir, "config", "Makefile")
 
@@ -518,66 +509,11 @@
         _config_vars['prefix'] = PREFIX
         _config_vars['exec_prefix'] = EXEC_PREFIX
 
+        # OS X platforms require special customization to handle
+        # multi-architecture, multi-os-version installers
         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', 'LDSHARED',
-                        # 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', 'LDSHARED',
-                        # 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.
-                #
-                m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS'])
-                if m is not None:
-                    sdk = m.group(1)
-                    if not os.path.exists(sdk):
-                        for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED',
-                             # 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
+            import _osx_support
+            _osx_support.customize_config_vars(_config_vars)
 
     if args:
         vals = []
diff --git a/lib-python/2.7/distutils/tests/test_build_ext.py b/lib-python/2.7/distutils/tests/test_build_ext.py
--- a/lib-python/2.7/distutils/tests/test_build_ext.py
+++ b/lib-python/2.7/distutils/tests/test_build_ext.py
@@ -77,8 +77,9 @@
         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)
+        if test_support.HAVE_DOCSTRINGS:
+            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))
 
diff --git a/lib-python/2.7/distutils/tests/test_dir_util.py b/lib-python/2.7/distutils/tests/test_dir_util.py
--- a/lib-python/2.7/distutils/tests/test_dir_util.py
+++ b/lib-python/2.7/distutils/tests/test_dir_util.py
@@ -101,6 +101,24 @@
         remove_tree(self.root_target, verbose=0)
         remove_tree(self.target2, verbose=0)
 
+    def test_copy_tree_skips_nfs_temp_files(self):
+        mkpath(self.target, verbose=0)
+
+        a_file = os.path.join(self.target, 'ok.txt')
+        nfs_file = os.path.join(self.target, '.nfs123abc')
+        for f in a_file, nfs_file:
+            fh = open(f, 'w')
+            try:
+                fh.write('some content')
+            finally:
+                fh.close()
+
+        copy_tree(self.target, self.target2)
+        self.assertEqual(os.listdir(self.target2), ['ok.txt'])
+
+        remove_tree(self.root_target, verbose=0)
+        remove_tree(self.target2, verbose=0)
+
     def test_ensure_relative(self):
         if os.sep == '/':
             self.assertEqual(ensure_relative('/home/foo'), 'home/foo')
diff --git a/lib-python/2.7/distutils/tests/test_msvc9compiler.py b/lib-python/2.7/distutils/tests/test_msvc9compiler.py
--- a/lib-python/2.7/distutils/tests/test_msvc9compiler.py
+++ b/lib-python/2.7/distutils/tests/test_msvc9compiler.py
@@ -104,7 +104,7 @@
                             unittest.TestCase):
 
     def test_no_compiler(self):
-        # makes sure query_vcvarsall throws
+        # makes sure query_vcvarsall raises
         # a DistutilsPlatformError if the compiler
         # is not found
         from distutils.msvc9compiler import query_vcvarsall
diff --git a/lib-python/2.7/distutils/tests/test_register.py b/lib-python/2.7/distutils/tests/test_register.py
--- a/lib-python/2.7/distutils/tests/test_register.py
+++ b/lib-python/2.7/distutils/tests/test_register.py
@@ -1,6 +1,5 @@
 # -*- encoding: utf8 -*-
 """Tests for distutils.command.register."""
-import sys
 import os
 import unittest
 import getpass
@@ -11,11 +10,14 @@
 
 from distutils.command import register as register_module
 from distutils.command.register import register
-from distutils.core import Distribution
 from distutils.errors import DistutilsSetupError
 
-from distutils.tests import support
-from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase
+from distutils.tests.test_config import PyPIRCCommandTestCase
+
+try:
+    import docutils
+except ImportError:
+    docutils = None
 
 PYPIRC_NOPASSWORD = """\
 [distutils]
@@ -192,6 +194,7 @@
         self.assertEqual(headers['Content-length'], '290')
         self.assertTrue('tarek' in req.data)
 
+    @unittest.skipUnless(docutils is not None, 'needs docutils')
     def test_strict(self):
         # testing the script option
         # when on, the register command stops if
@@ -204,13 +207,6 @@
         cmd.strict = 1
         self.assertRaises(DistutilsSetupError, cmd.run)
 
-        # we don't test the reSt feature if docutils
-        # is not installed
-        try:
-            import docutils
-        except ImportError:
-            return
-
         # metadata are OK but long_description is broken
         metadata = {'url': 'xxx', 'author': 'xxx',
                     'author_email': u'éxéxé',
@@ -264,6 +260,21 @@
         finally:
             del register_module.raw_input
 
+    @unittest.skipUnless(docutils is not None, 'needs docutils')
+    def test_register_invalid_long_description(self):
+        description = ':funkie:`str`'  # mimic Sphinx-specific markup
+        metadata = {'url': 'xxx', 'author': 'xxx',
+                    'author_email': 'xxx',
+                    'name': 'xxx', 'version': 'xxx',
+                    'long_description': description}
+        cmd = self._get_cmd(metadata)
+        cmd.ensure_finalized()
+        cmd.strict = True
+        inputs = RawInputs('2', 'tarek', 'tarek at ziade.org')
+        register_module.raw_input = inputs
+        self.addCleanup(delattr, register_module, 'raw_input')
+        self.assertRaises(DistutilsSetupError, cmd.run)
+
     def test_check_metadata_deprecated(self):
         # makes sure make_metadata is deprecated
         cmd = self._get_cmd()
diff --git a/lib-python/2.7/distutils/tests/test_sdist.py b/lib-python/2.7/distutils/tests/test_sdist.py
--- a/lib-python/2.7/distutils/tests/test_sdist.py
+++ b/lib-python/2.7/distutils/tests/test_sdist.py
@@ -91,9 +91,8 @@
 
     @unittest.skipUnless(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
-        # on all systems
+        # this test creates a project with some VCS dirs and an NFS rename
+        # file, then launches sdist to check they get pruned on all systems
 
         # creating VCS directories with some files in them
         os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
@@ -107,6 +106,8 @@
         self.write_file((self.tmp_dir, 'somecode', '.git',
                          'ok'), 'xxx')
 
+        self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx')
+
         # now building a sdist
         dist, cmd = self.get_cmd()
 
diff --git a/lib-python/2.7/distutils/tests/test_sysconfig.py b/lib-python/2.7/distutils/tests/test_sysconfig.py
--- a/lib-python/2.7/distutils/tests/test_sysconfig.py
+++ b/lib-python/2.7/distutils/tests/test_sysconfig.py
@@ -72,6 +72,35 @@
                              'OTHER': 'foo'})
 
 
+    def test_sysconfig_module(self):
+        import sysconfig as global_sysconfig
+        self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS'))
+        self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS'))
+
+    @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),'compiler flags customized')
+    def test_sysconfig_compiler_vars(self):
+        # On OS X, binary installers support extension module building on
+        # various levels of the operating system with differing Xcode
+        # configurations.  This requires customization of some of the
+        # compiler configuration directives to suit the environment on
+        # the installed machine.  Some of these customizations may require
+        # running external programs and, so, are deferred until needed by
+        # the first extension module build.  With Python 3.3, only
+        # the Distutils version of sysconfig is used for extension module
+        # builds, which happens earlier in the Distutils tests.  This may
+        # cause the following tests to fail since no tests have caused
+        # the global version of sysconfig to call the customization yet.
+        # The solution for now is to simply skip this test in this case.
+        # The longer-term solution is to only have one version of sysconfig.
+
+        import sysconfig as global_sysconfig
+        if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'):
+            return
+        self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED'))
+        self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC'))
+
+
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(SysconfigTestCase))
diff --git a/lib-python/2.7/distutils/unixccompiler.py b/lib-python/2.7/distutils/unixccompiler.py
--- a/lib-python/2.7/distutils/unixccompiler.py
+++ b/lib-python/2.7/distutils/unixccompiler.py
@@ -26,6 +26,9 @@
      DistutilsExecError, CompileError, LibError, LinkError
 from distutils import log
 
+if sys.platform == 'darwin':
+    import _osx_support
+
 # XXX Things not currently handled:
 #   * optimization/debug/warning flags; we just use whatever's in Python's
 #     Makefile and live with it.  Is this adequate?  If not, we might
@@ -41,68 +44,6 @@
 #     should just happily stuff them into the preprocessor/compiler/linker
 #     options and carry on.
 
-def _darwin_compiler_fixup(compiler_so, cc_args):
-    """
-    This function will strip '-isysroot PATH' and '-arch ARCH' from the
-    compile flags if the user has specified one them in extra_compile_flags.
-
-    This is needed because '-arch ARCH' adds another architecture to the
-    build, without a way to remove an architecture. Furthermore GCC will
-    barf if multiple '-isysroot' arguments are present.
-    """
-    stripArch = stripSysroot = 0
-
-    compiler_so = list(compiler_so)
-    kernel_version = os.uname()[2] # 8.4.3
-    major_version = int(kernel_version.split('.')[0])
-
-    if major_version < 8:
-        # OSX before 10.4.0, these don't support -arch and -isysroot at
-        # all.
-        stripArch = stripSysroot = True
-    else:
-        stripArch = '-arch' in cc_args
-        stripSysroot = '-isysroot' in cc_args
-
-    if stripArch or 'ARCHFLAGS' in os.environ:
-        while 1:
-            try:
-                index = compiler_so.index('-arch')
-                # Strip this argument and the next one:
-                del compiler_so[index:index+2]
-            except ValueError:
-                break
-
-    if 'ARCHFLAGS' in os.environ and not stripArch:
-        # User specified different -arch flags in the environ,
-        # see also distutils.sysconfig
-        compiler_so = compiler_so + os.environ['ARCHFLAGS'].split()
-
-    if stripSysroot:
-        try:
-            index = compiler_so.index('-isysroot')
-            # Strip this argument and the next one:
-            del compiler_so[index:index+2]
-        except ValueError:
-            pass
-
-    # Check if the SDK that is used during compilation actually exists,
-    # the universal build requires the usage of a universal SDK and not all
-    # users have that installed by default.
-    sysroot = None
-    if '-isysroot' in cc_args:
-        idx = cc_args.index('-isysroot')
-        sysroot = cc_args[idx+1]
-    elif '-isysroot' in compiler_so:
-        idx = compiler_so.index('-isysroot')
-        sysroot = compiler_so[idx+1]
-
-    if sysroot and not os.path.isdir(sysroot):
-        log.warn("Compiling with an SDK that doesn't seem to exist: %s",
-                sysroot)
-        log.warn("Please check your Xcode installation")
-
-    return compiler_so
 
 class UnixCCompiler(CCompiler):
 
@@ -172,7 +113,8 @@
     def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
         compiler_so = self.compiler_so
         if sys.platform == 'darwin':
-            compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs)
+            compiler_so = _osx_support.compiler_fixup(compiler_so,
+                                                    cc_args + extra_postargs)
         try:
             self.spawn(compiler_so + cc_args + [src, '-o', obj] +
                        extra_postargs)
@@ -251,7 +193,7 @@
                     linker[i] = self.compiler_cxx[i]
 
                 if sys.platform == 'darwin':
-                    linker = _darwin_compiler_fixup(linker, ld_args)
+                    linker = _osx_support.compiler_fixup(linker, ld_args)
 
                 self.spawn(linker + ld_args)
             except DistutilsExecError, msg:
diff --git a/lib-python/2.7/distutils/util.py b/lib-python/2.7/distutils/util.py
--- a/lib-python/2.7/distutils/util.py
+++ b/lib-python/2.7/distutils/util.py
@@ -51,6 +51,10 @@
             return 'win-ia64'
         return sys.platform
 
+    # Set for cross builds explicitly
+    if "_PYTHON_HOST_PLATFORM" in os.environ:
+        return os.environ["_PYTHON_HOST_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.
@@ -93,94 +97,10 @@
         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.
-        from distutils.sysconfig import get_config_vars
-        cfgvars = get_config_vars()
-
-        macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET')
-
-        if 1:
-            # 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())
-                    if m is not None:
-                        macrelease = '.'.join(m.group(1).split('.')[:2])
-                    # else: fall back to the default behaviour
-                finally:
-                    f.close()
-
-        if not macver:
-            macver = macrelease
-
-        if macver:
-            from distutils.sysconfig import get_config_vars
-            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.maxint >= 2**32:
-                    machine = 'x86_64'
-
-            elif machine in ('PowerPC', 'Power_Macintosh'):
-                # Pick a sane name for the PPC architecture.
-                machine = 'ppc'
-
-                # See 'i386' case
-                if sys.maxint >= 2**32:
-                    machine = 'ppc64'
+        import _osx_support, distutils.sysconfig
+        osname, release, machine = _osx_support.get_platform_osx(
+                                        distutils.sysconfig.get_config_vars(),
+                                        osname, release, machine)
 
     return "%s-%s-%s" % (osname, release, machine)
 
diff --git a/lib-python/2.7/doctest.py b/lib-python/2.7/doctest.py
--- a/lib-python/2.7/doctest.py
+++ b/lib-python/2.7/doctest.py
@@ -2314,7 +2314,8 @@
         return "Doctest: " + self._dt_test.name
 
 class SkipDocTestCase(DocTestCase):
-    def __init__(self):
+    def __init__(self, module):
+        self.module = module
         DocTestCase.__init__(self, None)
 
     def setUp(self):
@@ -2324,7 +2325,10 @@
         pass
 
     def shortDescription(self):
-        return "Skipping tests from %s" % module.__name__
+        return "Skipping tests from %s" % self.module.__name__
+
+    __str__ = shortDescription
+
 
 def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None,
                  **options):
@@ -2372,12 +2376,17 @@
     if not tests and sys.flags.optimize >=2:
         # Skip doctests when running with -O2
         suite = unittest.TestSuite()
-        suite.addTest(SkipDocTestCase())
+        suite.addTest(SkipDocTestCase(module))
         return suite
     elif not tests:
         # Why do we want to do this? Because it reveals a bug that might
         # otherwise be hidden.
-        raise ValueError(module, "has no tests")
+        # It is probably a bug that this exception is not also raised if the
+        # number of doctest examples in tests is zero (i.e. if no doctest
+        # examples were found).  However, we should probably not be raising
+        # an exception at all here, though it is too late to make this change
+        # for a maintenance release.  See also issue #14649.
+        raise ValueError(module, "has no docstrings")
 
     tests.sort()
     suite = unittest.TestSuite()
diff --git a/lib-python/2.7/email/_parseaddr.py b/lib-python/2.7/email/_parseaddr.py
--- a/lib-python/2.7/email/_parseaddr.py
+++ b/lib-python/2.7/email/_parseaddr.py
@@ -13,7 +13,7 @@
     'quote',
     ]
 
-import time
+import time, calendar
 
 SPACE = ' '
 EMPTYSTRING = ''
@@ -150,13 +150,13 @@
 
 
 def mktime_tz(data):
-    """Turn a 10-tuple as returned by parsedate_tz() into a UTC timestamp."""
+    """Turn a 10-tuple as returned by parsedate_tz() into a POSIX timestamp."""
     if data[9] is None:
         # No zone info, so localtime is better assumption than GMT
         return time.mktime(data[:8] + (-1,))
     else:
-        t = time.mktime(data[:8] + (0,))
-        return t - data[9] - time.timezone
+        t = calendar.timegm(data)
+        return t - data[9]
 
 
 def quote(str):
diff --git a/lib-python/2.7/email/base64mime.py b/lib-python/2.7/email/base64mime.py
--- a/lib-python/2.7/email/base64mime.py
+++ b/lib-python/2.7/email/base64mime.py
@@ -130,7 +130,7 @@
     verbatim (this is the default).
 
     Each line of encoded text will end with eol, which defaults to "\\n".  Set
-    this to "\r\n" if you will be using the result of this function directly
+    this to "\\r\\n" if you will be using the result of this function directly
     in an email.
     """
     if not s:
diff --git a/lib-python/2.7/email/feedparser.py b/lib-python/2.7/email/feedparser.py
--- a/lib-python/2.7/email/feedparser.py
+++ b/lib-python/2.7/email/feedparser.py
@@ -13,7 +13,7 @@
 data.  When you have no more data to push into the parser, call .close().
 This completes the parsing and returns the root message object.
 
-The other advantage of this parser is that it will never throw a parsing
+The other advantage of this parser is that it will never raise a parsing
 exception.  Instead, when it finds something unexpected, it adds a 'defect' to
 the current message.  Defects are just instances that live on the message
 object's .defects attribute.
@@ -214,7 +214,7 @@
         # supposed to see in the body of the message.
         self._parse_headers(headers)
         # Headers-only parsing is a backwards compatibility hack, which was
-        # necessary in the older parser, which could throw errors.  All
+        # necessary in the older parser, which could raise errors.  All
         # remaining lines in the input are thrown into the message body.
         if self._headersonly:
             lines = []
diff --git a/lib-python/2.7/email/generator.py b/lib-python/2.7/email/generator.py
--- a/lib-python/2.7/email/generator.py
+++ b/lib-python/2.7/email/generator.py
@@ -212,7 +212,11 @@
             msg.set_boundary(boundary)
         # If there's a preamble, write it out, with a trailing CRLF
         if msg.preamble is not None:
-            print >> self._fp, msg.preamble
+            if self._mangle_from_:
+                preamble = fcre.sub('>From ', msg.preamble)
+            else:
+                preamble = msg.preamble
+            print >> self._fp, preamble
         # dash-boundary transport-padding CRLF
         print >> self._fp, '--' + boundary
         # body-part
@@ -230,7 +234,11 @@
         self._fp.write('\n--' + boundary + '--')
         if msg.epilogue is not None:
             print >> self._fp
-            self._fp.write(msg.epilogue)
+            if self._mangle_from_:
+                epilogue = fcre.sub('>From ', msg.epilogue)
+            else:
+                epilogue = msg.epilogue
+            self._fp.write(epilogue)
 
     def _handle_multipart_signed(self, msg):
         # The contents of signed parts has to stay unmodified in order to keep
diff --git a/lib-python/2.7/email/test/test_email.py b/lib-python/2.7/email/test/test_email.py
--- a/lib-python/2.7/email/test/test_email.py
+++ b/lib-python/2.7/email/test/test_email.py
@@ -9,6 +9,7 @@
 import difflib
 import unittest
 import warnings
+import textwrap
 from cStringIO import StringIO
 
 import email
@@ -948,6 +949,28 @@
 Blah blah blah
 """)
 
+    def test_mangle_from_in_preamble_and_epilog(self):
+        s = StringIO()
+        g = Generator(s, mangle_from_=True)
+        msg = email.message_from_string(textwrap.dedent("""\
+            From: foo at bar.com
+            Mime-Version: 1.0
+            Content-Type: multipart/mixed; boundary=XXX
+
+            From somewhere unknown
+
+            --XXX
+            Content-Type: text/plain
+
+            foo
+
+            --XXX--
+
+            From somewhere unknowable
+            """))
+        g.flatten(msg)
+        self.assertEqual(len([1 for x in s.getvalue().split('\n')
+                                  if x.startswith('>From ')]), 2)
 
 
 # Test the basic MIMEAudio class
@@ -2262,6 +2285,12 @@
         eq(time.localtime(t)[:6], timetup[:6])
         eq(int(time.strftime('%Y', timetup[:9])), 2003)
 
+    def test_mktime_tz(self):
+        self.assertEqual(Utils.mktime_tz((1970, 1, 1, 0, 0, 0,
+                                          -1, -1, -1, 0)), 0)
+        self.assertEqual(Utils.mktime_tz((1970, 1, 1, 0, 0, 0,
+                                          -1, -1, -1, 1234)), -1234)
+
     def test_parsedate_y2k(self):
         """Test for parsing a date with a two-digit year.
 
diff --git a/lib-python/2.7/email/test/test_email_renamed.py b/lib-python/2.7/email/test/test_email_renamed.py
--- a/lib-python/2.7/email/test/test_email_renamed.py
+++ b/lib-python/2.7/email/test/test_email_renamed.py
@@ -994,6 +994,38 @@
         eq(msg.get_payload(), '+vv8/f7/')
         eq(msg.get_payload(decode=True), bytes)
 
+    def test_binary_body_with_encode_7or8bit(self):
+        # Issue 17171.
+        bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff'
+        msg = MIMEApplication(bytesdata, _encoder=encoders.encode_7or8bit)
+        # Treated as a string, this will be invalid code points.
+        self.assertEqual(msg.get_payload(), bytesdata)
+        self.assertEqual(msg.get_payload(decode=True), bytesdata)
+        self.assertEqual(msg['Content-Transfer-Encoding'], '8bit')
+        s = StringIO()
+        g = Generator(s)
+        g.flatten(msg)
+        wireform = s.getvalue()
+        msg2 = email.message_from_string(wireform)
+        self.assertEqual(msg.get_payload(), bytesdata)
+        self.assertEqual(msg2.get_payload(decode=True), bytesdata)
+        self.assertEqual(msg2['Content-Transfer-Encoding'], '8bit')
+
+    def test_binary_body_with_encode_noop(self):
+        # Issue 16564: This does not produce an RFC valid message, since to be
+        # valid it should have a CTE of binary.  But the below works, and is
+        # documented as working this way.
+        bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff'
+        msg = MIMEApplication(bytesdata, _encoder=encoders.encode_noop)
+        self.assertEqual(msg.get_payload(), bytesdata)
+        self.assertEqual(msg.get_payload(decode=True), bytesdata)
+        s = StringIO()
+        g = Generator(s)
+        g.flatten(msg)
+        wireform = s.getvalue()
+        msg2 = email.message_from_string(wireform)
+        self.assertEqual(msg.get_payload(), bytesdata)
+        self.assertEqual(msg2.get_payload(decode=True), bytesdata)
 
 
 # Test the basic MIMEText class
diff --git a/lib-python/2.7/email/utils.py b/lib-python/2.7/email/utils.py
--- a/lib-python/2.7/email/utils.py
+++ b/lib-python/2.7/email/utils.py
@@ -63,7 +63,7 @@
     """Decodes a base64 string.
 
     This function is equivalent to base64.decodestring and it's retained only
-    for backward compatibility. It used to remove the last \n of the decoded
+    for backward compatibility. It used to remove the last \\n of the decoded
     string, if it had any (see issue 7143).
     """
     if not s:
@@ -73,7 +73,7 @@
 
 
 def fix_eols(s):
-    """Replace all line-ending characters with \r\n."""
+    """Replace all line-ending characters with \\r\\n."""
     # Fix newlines with no preceding carriage return
     s = re.sub(r'(?<!\r)\n', CRLF, s)
     # Fix carriage returns with no following newline
diff --git a/lib-python/2.7/ftplib.py b/lib-python/2.7/ftplib.py
--- a/lib-python/2.7/ftplib.py
+++ b/lib-python/2.7/ftplib.py
@@ -273,21 +273,24 @@
 
     def makeport(self):
         '''Create a new socket and send a PORT command for it.'''
-        msg = "getaddrinfo returns an empty list"
+        err = None
         sock = None
         for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
             af, socktype, proto, canonname, sa = res
             try:
                 sock = socket.socket(af, socktype, proto)
                 sock.bind(sa)
-            except socket.error, msg:
+            except socket.error, err:
                 if sock:
                     sock.close()
                 sock = None
                 continue
             break
-        if not sock:
-            raise socket.error, msg
+        if sock is None:
+            if err is not None:
+                raise err
+            else:
+                raise socket.error("getaddrinfo returns an empty list")
         sock.listen(1)
         port = sock.getsockname()[1] # Get proper port
         host = self.sock.getsockname()[0] # Get proper host
diff --git a/lib-python/2.7/glob.py b/lib-python/2.7/glob.py
--- a/lib-python/2.7/glob.py
+++ b/lib-python/2.7/glob.py
@@ -5,12 +5,23 @@
 import re
 import fnmatch
 
+try:
+    _unicode = unicode
+except NameError:
+    # If Python is built without Unicode support, the unicode type
+    # will not exist. Fake one.
+    class _unicode(object):
+        pass
+
 __all__ = ["glob", "iglob"]
 
 def glob(pathname):
     """Return a list of paths matching a pathname pattern.
 
-    The pattern may contain simple shell-style wildcards a la fnmatch.
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
 
     """
     return list(iglob(pathname))
@@ -18,7 +29,10 @@
 def iglob(pathname):
     """Return an iterator which yields the paths matching a pathname pattern.
 
-    The pattern may contain simple shell-style wildcards a la fnmatch.
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
 
     """
     if not has_magic(pathname):
@@ -30,7 +44,10 @@
         for name in glob1(os.curdir, basename):
             yield name
         return
-    if has_magic(dirname):
+    # `os.path.split()` returns the argument itself as a dirname if it is a
+    # drive or UNC path.  Prevent an infinite recursion if a drive or UNC path
+    # contains magic characters (i.e. r'\\?\C:').
+    if dirname != pathname and has_magic(dirname):
         dirs = iglob(dirname)
     else:
         dirs = [dirname]
@@ -49,7 +66,7 @@
 def glob1(dirname, pattern):
     if not dirname:
         dirname = os.curdir
-    if isinstance(pattern, unicode) and not isinstance(dirname, unicode):
+    if isinstance(pattern, _unicode) and not isinstance(dirname, unicode):
         dirname = unicode(dirname, sys.getfilesystemencoding() or
                                    sys.getdefaultencoding())
     try:
diff --git a/lib-python/2.7/gzip.py b/lib-python/2.7/gzip.py
--- a/lib-python/2.7/gzip.py
+++ b/lib-python/2.7/gzip.py
@@ -21,9 +21,6 @@
     # or unsigned.
     output.write(struct.pack("<L", value))
 
-def read32(input):
-    return struct.unpack("<I", input.read(4))[0]
-
 def open(filename, mode="rb", compresslevel=9):
     """Shorthand for GzipFile(filename, mode, compresslevel).
 
@@ -66,9 +63,10 @@
         Be aware that only the 'rb', 'ab', and 'wb' values should be used
         for cross-platform portability.
 
-        The compresslevel argument is an integer from 1 to 9 controlling the
+        The compresslevel argument is an integer from 0 to 9 controlling the
         level of compression; 1 is fastest and produces the least compression,
-        and 9 is slowest and produces the most compression.  The default is 9.
+        and 9 is slowest and produces the most compression. 0 is no compression
+        at all. The default is 9.
 
         The mtime argument is an optional numeric timestamp to be written
         to the stream when compressing.  All gzip compressed streams
@@ -81,6 +79,10 @@
 
         """
 
+        # Make sure we don't inadvertently enable universal newlines on the
+        # underlying file object - in read mode, this causes data corruption.
+        if mode:
+            mode = mode.replace('U', '')
         # guarantee the file is opened in binary mode on platforms
         # that care about that sort of thing
         if mode and 'b' not in mode:
@@ -179,24 +181,28 @@
         self.crc = zlib.crc32("") & 0xffffffffL
         self.size = 0
 
+    def _read_exact(self, n):
+        data = self.fileobj.read(n)
+        while len(data) < n:
+            b = self.fileobj.read(n - len(data))
+            if not b:
+                raise EOFError("Compressed file ended before the "
+                               "end-of-stream marker was reached")
+            data += b
+        return data
+
     def _read_gzip_header(self):
         magic = self.fileobj.read(2)
         if magic != '\037\213':
             raise IOError, 'Not a gzipped file'
-        method = ord( self.fileobj.read(1) )
+
+        method, flag, self.mtime = struct.unpack("<BBIxx", self._read_exact(8))
         if method != 8:
             raise IOError, 'Unknown compression method'
-        flag = ord( self.fileobj.read(1) )
-        self.mtime = read32(self.fileobj)
-        # extraflag = self.fileobj.read(1)
-        # os = self.fileobj.read(1)
-        self.fileobj.read(2)
 
         if flag & FEXTRA:
             # Read & discard the extra field, if present
-            xlen = ord(self.fileobj.read(1))
-            xlen = xlen + 256*ord(self.fileobj.read(1))
-            self.fileobj.read(xlen)
+            self._read_exact(struct.unpack("<H", self._read_exact(2)))
         if flag & FNAME:
             # Read and discard a null-terminated string containing the filename
             while True:
@@ -210,7 +216,7 @@
                 if not s or s=='\000':
                     break
         if flag & FHCRC:
-            self.fileobj.read(2)     # Read & discard the 16-bit header CRC
+            self._read_exact(2)     # Read & discard the 16-bit header CRC
 
     def write(self,data):
         self._check_closed()
@@ -244,20 +250,16 @@
 
         readsize = 1024
         if size < 0:        # get the whole thing
-            try:
-                while True:
-                    self._read(readsize)
-                    readsize = min(self.max_read_chunk, readsize * 2)
-            except EOFError:
-                size = self.extrasize
+            while self._read(readsize):
+                readsize = min(self.max_read_chunk, readsize * 2)
+            size = self.extrasize
         else:               # just get some more of it
-            try:
-                while size > self.extrasize:
-                    self._read(readsize)
-                    readsize = min(self.max_read_chunk, readsize * 2)
-            except EOFError:
-                if size > self.extrasize:
-                    size = self.extrasize
+            while size > self.extrasize:
+                if not self._read(readsize):
+                    if size > self.extrasize:
+                        size = self.extrasize
+                    break
+                readsize = min(self.max_read_chunk, readsize * 2)
 
         offset = self.offset - self.extrastart
         chunk = self.extrabuf[offset: offset + size]
@@ -272,7 +274,7 @@
 
     def _read(self, size=1024):
         if self.fileobj is None:
-            raise EOFError, "Reached EOF"
+            return False
 
         if self._new_member:
             # If the _new_member flag is set, we have to
@@ -283,7 +285,7 @@
             pos = self.fileobj.tell()   # Save current position
             self.fileobj.seek(0, 2)     # Seek to end of file
             if pos == self.fileobj.tell():
-                raise EOFError, "Reached EOF"
+                return False
             else:
                 self.fileobj.seek( pos ) # Return to original position
 
@@ -300,9 +302,10 @@
 
         if buf == "":
             uncompress = self.decompress.flush()
+            self.fileobj.seek(-len(self.decompress.unused_data), 1)
             self._read_eof()
             self._add_read_data( uncompress )
-            raise EOFError, 'Reached EOF'
+            return False
 
         uncompress = self.decompress.decompress(buf)
         self._add_read_data( uncompress )
@@ -312,13 +315,14 @@
             # so seek back to the start of the unused data, finish up
             # this member, and read a new gzip header.
             # (The number of bytes to seek back is the length of the unused
-            # data, minus 8 because _read_eof() will rewind a further 8 bytes)
-            self.fileobj.seek( -len(self.decompress.unused_data)+8, 1)
+            # data)
+            self.fileobj.seek(-len(self.decompress.unused_data), 1)
 
             # Check the CRC and file size, and set the flag so we read
             # a new member on the next call
             self._read_eof()
             self._new_member = True
+        return True
 
     def _add_read_data(self, data):
         self.crc = zlib.crc32(data, self.crc) & 0xffffffffL
@@ -329,14 +333,11 @@
         self.size = self.size + len(data)
 
     def _read_eof(self):
-        # We've read to the end of the file, so we have to rewind in order
-        # to reread the 8 bytes containing the CRC and the file size.
+        # We've read to the end of the file.
         # We check the that the computed CRC and size of the
         # uncompressed data matches the stored values.  Note that the size
         # stored is the true file size mod 2**32.
-        self.fileobj.seek(-8, 1)
-        crc32 = read32(self.fileobj)
-        isize = read32(self.fileobj)  # may exceed 2GB
+        crc32, isize = struct.unpack("<II", self._read_exact(8))
         if crc32 != self.crc:
             raise IOError("CRC check failed %s != %s" % (hex(crc32),
                                                          hex(self.crc)))
@@ -417,7 +418,7 @@
             if offset < self.offset:
                 raise IOError('Negative seek in write mode')
             count = offset - self.offset
-            for i in range(count // 1024):
+            for i in xrange(count // 1024):
                 self.write(1024 * '\0')
             self.write((count % 1024) * '\0')
         elif self.mode == READ:
@@ -425,7 +426,7 @@
                 # for negative seek, rewind and do positive seek
                 self.rewind()
             count = offset - self.offset
-            for i in range(count // 1024):
+            for i in xrange(count // 1024):
                 self.read(1024)
             self.read(count % 1024)
 
diff --git a/lib-python/2.7/hashlib.py b/lib-python/2.7/hashlib.py
--- a/lib-python/2.7/hashlib.py
+++ b/lib-python/2.7/hashlib.py
@@ -88,7 +88,7 @@
     except ImportError:
         pass  # no extension module, this hash is unsupported.
 
-    raise ValueError('unsupported hash type %s' % name)
+    raise ValueError('unsupported hash type ' + name)
 
 
 def __get_openssl_constructor(name):
diff --git a/lib-python/2.7/heapq.py b/lib-python/2.7/heapq.py
--- a/lib-python/2.7/heapq.py
+++ b/lib-python/2.7/heapq.py
@@ -129,9 +129,8 @@
 __all__ = ['heappush', 'heappop', 'heapify', 'heapreplace', 'merge',
            'nlargest', 'nsmallest', 'heappushpop']
 
-from itertools import islice, repeat, count, imap, izip, tee, chain
+from itertools import islice, count, imap, izip, tee, chain
 from operator import itemgetter
-import bisect
 
 def cmp_lt(x, y):
     # Use __lt__ if available; otherwise, try __le__.
@@ -188,6 +187,19 @@
     for i in reversed(xrange(n//2)):
         _siftup(x, i)
 
+def _heappushpop_max(heap, item):
+    """Maxheap version of a heappush followed by a heappop."""
+    if heap and cmp_lt(item, heap[0]):
+        item, heap[0] = heap[0], item
+        _siftup_max(heap, 0)
+    return item
+
+def _heapify_max(x):
+    """Transform list into a maxheap, in-place, in O(len(x)) time."""
+    n = len(x)
+    for i in reversed(range(n//2)):
+        _siftup_max(x, i)
+
 def nlargest(n, iterable):
     """Find the n largest elements in a dataset.
 
@@ -213,30 +225,16 @@
     """
     if n < 0:
         return []
-    if hasattr(iterable, '__len__') and n * 10 <= len(iterable):
-        # For smaller values of n, the bisect method is faster than a minheap.
-        # It is also memory efficient, consuming only n elements of space.
-        it = iter(iterable)
-        result = sorted(islice(it, 0, n))
-        if not result:
-            return result
-        insort = bisect.insort
-        pop = result.pop
-        los = result[-1]    # los --> Largest of the nsmallest
-        for elem in it:
-            if cmp_lt(elem, los):
-                insort(result, elem)
-                pop()
-                los = result[-1]
+    it = iter(iterable)
+    result = list(islice(it, n))
+    if not result:
         return result
-    # An alternative approach manifests the whole iterable in memory but
-    # saves comparisons by heapifying all at once.  Also, saves time
-    # over bisect.insort() which has O(n) data movement time for every
-    # insertion.  Finding the n smallest of an m length iterable requires
-    #    O(m) + O(n log m) comparisons.
-    h = list(iterable)
-    heapify(h)
-    return map(heappop, repeat(h, min(n, len(h))))
+    _heapify_max(result)
+    _heappushpop = _heappushpop_max
+    for elem in it:
+        _heappushpop(result, elem)
+    result.sort()
+    return result
 
 # 'heap' is a heap at all indices >= startpos, except possibly for pos.  pos
 # is the index of a leaf with a possibly out-of-order value.  Restore the
@@ -314,6 +312,42 @@
     heap[pos] = newitem
     _siftdown(heap, startpos, pos)
 
+def _siftdown_max(heap, startpos, pos):
+    'Maxheap variant of _siftdown'
+    newitem = heap[pos]
+    # Follow the path to the root, moving parents down until finding a place
+    # newitem fits.
+    while pos > startpos:
+        parentpos = (pos - 1) >> 1
+        parent = heap[parentpos]
+        if cmp_lt(parent, newitem):
+            heap[pos] = parent
+            pos = parentpos
+            continue
+        break
+    heap[pos] = newitem
+
+def _siftup_max(heap, pos):
+    'Maxheap variant of _siftup'
+    endpos = len(heap)
+    startpos = pos
+    newitem = heap[pos]
+    # Bubble up the larger child until hitting a leaf.
+    childpos = 2*pos + 1    # leftmost child position
+    while childpos < endpos:
+        # Set childpos to index of larger child.
+        rightpos = childpos + 1
+        if rightpos < endpos and not cmp_lt(heap[rightpos], heap[childpos]):
+            childpos = rightpos
+        # Move the larger child up.
+        heap[pos] = heap[childpos]
+        pos = childpos
+        childpos = 2*pos + 1
+    # The leaf at pos is empty now.  Put newitem there, and bubble it up
+    # to its final resting place (by sifting its parents down).
+    heap[pos] = newitem
+    _siftdown_max(heap, startpos, pos)
+
 # If available, use C implementation
 try:
     from _heapq import *
diff --git a/lib-python/2.7/httplib.py b/lib-python/2.7/httplib.py
--- a/lib-python/2.7/httplib.py
+++ b/lib-python/2.7/httplib.py
@@ -362,7 +362,9 @@
 
     def _read_status(self):
         # Initialize with Simple-Response defaults
-        line = self.fp.readline()
+        line = self.fp.readline(_MAXLINE + 1)
+        if len(line) > _MAXLINE:
+            raise LineTooLong("header line")
         if self.debuglevel > 0:
             print "reply:", repr(line)
         if not line:
@@ -545,7 +547,11 @@
             if self.length is None:
                 s = self.fp.read()
             else:
-                s = self._safe_read(self.length)
+                try:
+                    s = self._safe_read(self.length)
+                except IncompleteRead:
+                    self.close()
+                    raise
                 self.length = 0
             self.close()        # we read everything
             return s
@@ -559,10 +565,15 @@
         # connection, and the user is reading more bytes than will be provided
         # (for example, reading in 1k chunks)
         s = self.fp.read(amt)
+        if not s:
+            # Ideally, we would raise IncompleteRead if the content-length
+            # wasn't satisfied, but it might break compatibility.
+            self.close()
         if self.length is not None:
             self.length -= len(s)
             if not self.length:
                 self.close()
+
         return s
 
     def _read_chunked(self, amt):
@@ -748,7 +759,11 @@
             line = response.fp.readline(_MAXLINE + 1)
             if len(line) > _MAXLINE:
                 raise LineTooLong("header line")
-            if line == '\r\n': break
+            if not line:
+                # for sites which EOF without sending trailer
+                break
+            if line == '\r\n':
+                break
 
 
     def connect(self):
@@ -985,7 +1000,7 @@
 
         self.putrequest(method, url, **skips)
 
-        if body and ('content-length' not in header_names):
+        if body is not None and 'content-length' not in header_names:
             self._set_content_length(body)
         for hdr, value in headers.iteritems():
             self.putheader(hdr, value)
@@ -1058,7 +1073,7 @@
         if port == 0:
             port = None
 
-        # Note that we may pass an empty string as the host; this will throw
+        # Note that we may pass an empty string as the host; this will raise
         # an error when we attempt to connect. Presumably, the client code
         # will call connect before then, with a proper host.
         self._setup(self._connection_class(host, port, strict))
diff --git a/lib-python/2.7/idlelib/CallTips.py b/lib-python/2.7/idlelib/CallTips.py
--- a/lib-python/2.7/idlelib/CallTips.py
+++ b/lib-python/2.7/idlelib/CallTips.py
@@ -71,16 +71,16 @@
         if not sur_paren:
             return
         hp.set_index(sur_paren[0])
-        name = hp.get_expression()
-        if not name or (not evalfuncs and name.find('(') != -1):
+        expression = hp.get_expression()
+        if not expression or (not evalfuncs and expression.find('(') != -1):
             return
-        arg_text = self.fetch_tip(name)
+        arg_text = self.fetch_tip(expression)
         if not arg_text:
             return
         self.calltip = self._make_calltip_window()
         self.calltip.showtip(arg_text, sur_paren[0], sur_paren[1])
 
-    def fetch_tip(self, name):
+    def fetch_tip(self, expression):
         """Return the argument list and docstring of a function or class
 
         If there is a Python subprocess, get the calltip there.  Otherwise,
@@ -96,23 +96,27 @@
         """
         try:
             rpcclt = self.editwin.flist.pyshell.interp.rpcclt
-        except:
+        except AttributeError:
             rpcclt = None
         if rpcclt:
             return rpcclt.remotecall("exec", "get_the_calltip",
-                                     (name,), {})
+                                     (expression,), {})
         else:
-            entity = self.get_entity(name)
+            entity = self.get_entity(expression)
             return get_arg_text(entity)
 
-    def get_entity(self, name):
-        "Lookup name in a namespace spanning sys.modules and __main.dict__"
-        if name:
+    def get_entity(self, expression):
+        """Return the object corresponding to expression evaluated
+        in a namespace spanning sys.modules and __main.dict__.
+        """
+        if expression:
             namespace = sys.modules.copy()
             namespace.update(__main__.__dict__)
             try:
-                return eval(name, namespace)
-            except (NameError, AttributeError):
+                return eval(expression, namespace)
+            except BaseException:
+                # An uncaught exception closes idle, and eval can raise any
+                # exception, especially if user classes are involved.
                 return None
 
 def _find_constructor(class_ob):
@@ -127,9 +131,10 @@
     return None
 
 def get_arg_text(ob):
-    """Get a string describing the arguments for the given object"""
+    """Get a string describing the arguments for the given object,
+       only if it is callable."""
     arg_text = ""
-    if ob is not None:
+    if ob is not None and hasattr(ob, '__call__'):
         arg_offset = 0
         if type(ob) in (types.ClassType, types.TypeType):
             # Look for the highest __init__ in the class chain.
diff --git a/lib-python/2.7/idlelib/ColorDelegator.py b/lib-python/2.7/idlelib/ColorDelegator.py
--- a/lib-python/2.7/idlelib/ColorDelegator.py
+++ b/lib-python/2.7/idlelib/ColorDelegator.py
@@ -20,10 +20,11 @@
     # 1st 'file' colorized normal, 2nd as builtin, 3rd as string
     builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
     comment = any("COMMENT", [r"#[^\n]*"])
-    sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
-    dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
-    sq3string = r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
-    dq3string = r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
+    stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR)?"
+    sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?"
+    dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?'
+    sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
+    dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
     string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
     return kw + "|" + builtin + "|" + comment + "|" + string +\
            "|" + any("SYNC", [r"\n"])
diff --git a/lib-python/2.7/idlelib/EditorWindow.py b/lib-python/2.7/idlelib/EditorWindow.py
--- a/lib-python/2.7/idlelib/EditorWindow.py
+++ b/lib-python/2.7/idlelib/EditorWindow.py
@@ -172,13 +172,13 @@
                 'recent-files.lst')
         self.text_frame = text_frame = Frame(top)
         self.vbar = vbar = Scrollbar(text_frame, name='vbar')
-        self.width = idleConf.GetOption('main','EditorWindow','width')
+        self.width = idleConf.GetOption('main','EditorWindow','width', type='int')
         text_options = {
                 'name': 'text',
                 'padx': 5,
                 'wrap': 'none',
                 'width': self.width,
-                'height': idleConf.GetOption('main', 'EditorWindow', 'height')}
+                'height': idleConf.GetOption('main', 'EditorWindow', 'height', type='int')}
         if TkVersion >= 8.5:
             # Starting with tk 8.5 we have to set the new tabstyle option
             # to 'wordprocessor' to achieve the same display of tabs as in
@@ -255,7 +255,8 @@
         if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):
             fontWeight='bold'
         text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'),
-                          idleConf.GetOption('main', 'EditorWindow', 'font-size'),
+                          idleConf.GetOption('main', 'EditorWindow',
+                                             'font-size', type='int'),
                           fontWeight))
         text_frame.pack(side=LEFT, fill=BOTH, expand=1)
         text.pack(side=TOP, fill=BOTH, expand=1)
@@ -470,7 +471,6 @@
     rmenu = None
 
     def right_menu_event(self, event):
-        self.text.tag_remove("sel", "1.0", "end")
         self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
         if not self.rmenu:
             self.make_rmenu()
@@ -479,23 +479,52 @@
         iswin = sys.platform[:3] == 'win'
         if iswin:
             self.text.config(cursor="arrow")
+
+        for label, eventname, verify_state in self.rmenu_specs:
+            if verify_state is None:
+                continue
+            state = getattr(self, verify_state)()
+            rmenu.entryconfigure(label, state=state)
+
         rmenu.tk_popup(event.x_root, event.y_root)
         if iswin:
             self.text.config(cursor="ibeam")
 
     rmenu_specs = [
-        # ("Label", "<<virtual-event>>"), ...
-        ("Close", "<<close-window>>"), # Example
+        # ("Label", "<<virtual-event>>", "statefuncname"), ...
+        ("Close", "<<close-window>>", None), # Example
     ]
 
     def make_rmenu(self):
         rmenu = Menu(self.text, tearoff=0)
-        for label, eventname in self.rmenu_specs:
-            def command(text=self.text, eventname=eventname):
-                text.event_generate(eventname)
-            rmenu.add_command(label=label, command=command)
+        for label, eventname, _ in self.rmenu_specs:
+            if label is not None:
+                def command(text=self.text, eventname=eventname):
+                    text.event_generate(eventname)
+                rmenu.add_command(label=label, command=command)
+            else:
+                rmenu.add_separator()
         self.rmenu = rmenu
 
+    def rmenu_check_cut(self):
+        return self.rmenu_check_copy()
+
+    def rmenu_check_copy(self):
+        try:
+            indx = self.text.index('sel.first')
+        except TclError:
+            return 'disabled'
+        else:
+            return 'normal' if indx else 'disabled'
+
+    def rmenu_check_paste(self):
+        try:
+            self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD')
+        except TclError:
+            return 'disabled'
+        else:
+            return 'normal'
+
     def about_dialog(self, event=None):
         aboutDialog.AboutDialog(self.top,'About IDLE')
 
@@ -735,7 +764,8 @@
         if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
             fontWeight='bold'
         self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
-                idleConf.GetOption('main','EditorWindow','font-size'),
+                idleConf.GetOption('main','EditorWindow','font-size',
+                                   type='int'),
                 fontWeight))
 
     def RemoveKeybindings(self):
@@ -856,7 +886,7 @@
         # for each edit window instance, construct the recent files menu
         for instance in self.top.instance_dict.keys():
             menu = instance.recent_files_menu
-            menu.delete(1, END)  # clear, and rebuild:
+            menu.delete(0, END)  # clear, and rebuild:
             for i, file_name in enumerate(rf_list):
                 file_name = file_name.rstrip()  # zap \n
                 # make unicode string to display non-ASCII chars correctly
@@ -1581,7 +1611,7 @@
         try:
             try:
                 _tokenize.tokenize(self.readline, self.tokeneater)
-            except _tokenize.TokenError:
+            except (_tokenize.TokenError, SyntaxError):
                 # since we cut off the tokenizer early, we can trigger
                 # spurious errors
                 pass
diff --git a/lib-python/2.7/idlelib/FormatParagraph.py b/lib-python/2.7/idlelib/FormatParagraph.py
--- a/lib-python/2.7/idlelib/FormatParagraph.py
+++ b/lib-python/2.7/idlelib/FormatParagraph.py
@@ -32,7 +32,8 @@
         self.editwin = None
 
     def format_paragraph_event(self, event):
-        maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph'))
+        maxformatwidth = int(idleConf.GetOption('main','FormatParagraph',
+                                                'paragraph', type='int'))
         text = self.editwin.text
         first, last = self.editwin.get_selection_indices()
         if first and last:
diff --git a/lib-python/2.7/idlelib/HyperParser.py b/lib-python/2.7/idlelib/HyperParser.py
--- a/lib-python/2.7/idlelib/HyperParser.py
+++ b/lib-python/2.7/idlelib/HyperParser.py
@@ -232,6 +232,11 @@
                     pass
                 else:
                     # We can't continue after other types of brackets
+                    if rawtext[pos] in "'\"":
+                        # Scan a string prefix
+                        while pos > 0 and rawtext[pos - 1] in "rRbBuU":
+                            pos -= 1
+                        last_identifier_pos = pos
                     break
 
             else:
diff --git a/lib-python/2.7/idlelib/IOBinding.py b/lib-python/2.7/idlelib/IOBinding.py
--- a/lib-python/2.7/idlelib/IOBinding.py
+++ b/lib-python/2.7/idlelib/IOBinding.py
@@ -7,6 +7,7 @@
 
 import os
 import types
+import pipes
 import sys
 import codecs
 import tempfile
@@ -196,29 +197,33 @@
                 self.filename_change_hook()
 
     def open(self, event=None, editFile=None):
-        if self.editwin.flist:
+        flist = self.editwin.flist
+        # Save in case parent window is closed (ie, during askopenfile()).
+        if flist:
             if not editFile:
                 filename = self.askopenfile()
             else:
                 filename=editFile
             if filename:
-                # If the current window has no filename and hasn't been
-                # modified, we replace its contents (no loss).  Otherwise
-                # we open a new window.  But we won't replace the
-                # shell window (which has an interp(reter) attribute), which
-                # gets set to "not modified" at every new prompt.
-                try:
-                    interp = self.editwin.interp
-                except AttributeError:
-                    interp = None
-                if not self.filename and self.get_saved() and not interp:
-                    self.editwin.flist.open(filename, self.loadfile)
+                # If editFile is valid and already open, flist.open will
+                # shift focus to its existing window.
+                # If the current window exists and is a fresh unnamed,
+                # unmodified editor window (not an interpreter shell),
+                # pass self.loadfile to flist.open so it will load the file
+                # in the current window (if the file is not already open)
+                # instead of a new window.
+                if (self.editwin and
+                        not getattr(self.editwin, 'interp', None) and
+                        not self.filename and
+                        self.get_saved()):
+                    flist.open(filename, self.loadfile)
                 else:
-                    self.editwin.flist.open(filename)
+                    flist.open(filename)
             else:
-                self.text.focus_set()
+                if self.text:
+                    self.text.focus_set()
             return "break"
-        #
+
         # Code for use outside IDLE:
         if self.get_saved():
             reply = self.maybesave()
@@ -499,7 +504,7 @@
         else: #no printing for this platform
             printPlatform = False
         if printPlatform:  #we can try to print for this platform
-            command = command % filename
+            command = command % pipes.quote(filename)
             pipe = os.popen(command, "r")
             # things can get ugly on NT if there is no printer available.
             output = pipe.read().strip()
diff --git a/lib-python/2.7/idlelib/NEWS.txt b/lib-python/2.7/idlelib/NEWS.txt
--- a/lib-python/2.7/idlelib/NEWS.txt
+++ b/lib-python/2.7/idlelib/NEWS.txt
@@ -1,5 +1,38 @@
+What's New in IDLE 2.7.4?
+=========================
+
+- Issue #15318: Prevent writing to sys.stdin.
+
+- Issue #13532, #15319: Check that arguments to sys.stdout.write are strings.
+
+- Issue # 12510: Attempt to get certain tool tips no longer crashes IDLE.
+
+- Issue10365: File open dialog now works instead of crashing even when
+  parent window is closed while dialog is open.
+
+- Issue 14876: use user-selected font for highlight configuration.
+
+- Issue #14018: Update checks for unstable system Tcl/Tk versions on OS X
+  to include versions shipped with OS X 10.7 and 10.8 in addition to 10.6.
+
+- Issue #15853: Prevent IDLE crash on OS X when opening Preferences menu
+  with certain versions of Tk 8.5.  Initial patch by Kevin Walzer.
+
+
+What's New in IDLE 2.7.3?
+=========================
+
+- Issue #14409: IDLE now properly executes commands in the Shell window
+  when it cannot read the normal config files on startup and
+  has to use the built-in default key bindings.
+  There was previously a bug in one of the defaults.
+
+- Issue #3573: IDLE hangs when passing invalid command line args
+  (directory(ies) instead of file(s)).
+
+
 What's New in IDLE 2.7.2?
-=======================
+=========================
 
 *Release date: 29-May-2011*
 
diff --git a/lib-python/2.7/idlelib/OutputWindow.py b/lib-python/2.7/idlelib/OutputWindow.py
--- a/lib-python/2.7/idlelib/OutputWindow.py
+++ b/lib-python/2.7/idlelib/OutputWindow.py
@@ -57,7 +57,11 @@
     # Our own right-button menu
 
     rmenu_specs = [
-        ("Go to file/line", "<<goto-file-line>>"),
+        ("Cut", "<<cut>>", "rmenu_check_cut"),
+        ("Copy", "<<copy>>", "rmenu_check_copy"),
+        ("Paste", "<<paste>>", "rmenu_check_paste"),
+        (None, None, None),
+        ("Go to file/line", "<<goto-file-line>>", None),
     ]
 
     file_line_pats = [
diff --git a/lib-python/2.7/idlelib/PyShell.py b/lib-python/2.7/idlelib/PyShell.py
--- a/lib-python/2.7/idlelib/PyShell.py
+++ b/lib-python/2.7/idlelib/PyShell.py
@@ -11,6 +11,7 @@
 import threading
 import traceback
 import types
+import io
 
 import linecache
 from code import InteractiveInterpreter
@@ -121,8 +122,13 @@
             old_hook()
         self.io.set_filename_change_hook(filename_changed_hook)
 
-    rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"),
-                   ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
+    rmenu_specs = [
+        ("Cut", "<<cut>>", "rmenu_check_cut"),
+        ("Copy", "<<copy>>", "rmenu_check_copy"),
+        ("Paste", "<<paste>>", "rmenu_check_paste"),
+        ("Set Breakpoint", "<<set-breakpoint-here>>", None),
+        ("Clear Breakpoint", "<<clear-breakpoint-here>>", None)
+    ]
 
     def set_breakpoint(self, lineno):
         text = self.text
@@ -251,8 +257,8 @@
     def ranges_to_linenumbers(self, ranges):
         lines = []
         for index in range(0, len(ranges), 2):
-            lineno = int(float(ranges[index]))
-            end = int(float(ranges[index+1]))
+            lineno = int(float(ranges[index].string))
+            end = int(float(ranges[index+1].string))
             while lineno < end:
                 lines.append(lineno)
                 lineno += 1
@@ -313,6 +319,11 @@
             "console": idleConf.GetHighlight(theme, "console"),
         })
 
+    def removecolors(self):
+        # Don't remove shell color tags before "iomark"
+        for tag in self.tagdefs:
+            self.tag_remove(tag, "iomark", "end")
+
 class ModifiedUndoDelegator(UndoDelegator):
     "Extend base class: forbid insert/delete before the I/O mark"
 
@@ -417,7 +428,8 @@
         except socket.timeout, err:
             self.display_no_subprocess_error()
             return None
-        self.rpcclt.register("stdin", self.tkconsole)
+        self.rpcclt.register("console", self.tkconsole)
+        self.rpcclt.register("stdin", self.tkconsole.stdin)
         self.rpcclt.register("stdout", self.tkconsole.stdout)
         self.rpcclt.register("stderr", self.tkconsole.stderr)
         self.rpcclt.register("flist", self.tkconsole.flist)
@@ -870,13 +882,14 @@
         self.save_stderr = sys.stderr
         self.save_stdin = sys.stdin
         from idlelib import IOBinding
-        self.stdout = PseudoFile(self, "stdout", IOBinding.encoding)
-        self.stderr = PseudoFile(self, "stderr", IOBinding.encoding)
-        self.console = PseudoFile(self, "console", IOBinding.encoding)
+        self.stdin = PseudoInputFile(self, "stdin", IOBinding.encoding)
+        self.stdout = PseudoOutputFile(self, "stdout", IOBinding.encoding)
+        self.stderr = PseudoOutputFile(self, "stderr", IOBinding.encoding)
+        self.console = PseudoOutputFile(self, "console", IOBinding.encoding)
         if not use_subprocess:
             sys.stdout = self.stdout
             sys.stderr = self.stderr
-            sys.stdin = self
+            sys.stdin = self.stdin
         #
         self.history = self.History(self.text)
         #
@@ -1251,28 +1264,98 @@
             if not use_subprocess:
                 raise KeyboardInterrupt
 
-class PseudoFile(object):
+    def rmenu_check_cut(self):
+        try:
+            if self.text.compare('sel.first', '<', 'iomark'):
+                return 'disabled'
+        except TclError: # no selection, so the index 'sel.first' doesn't exist
+            return 'disabled'
+        return super(PyShell, self).rmenu_check_cut()
+
+    def rmenu_check_paste(self):
+        if self.text.compare('insert', '<', 'iomark'):
+            return 'disabled'
+        return super(PyShell, self).rmenu_check_paste()
+
+class PseudoFile(io.TextIOBase):
 
     def __init__(self, shell, tags, encoding=None):
         self.shell = shell
         self.tags = tags
         self.softspace = 0
-        self.encoding = encoding
+        self._encoding = encoding
 
-    def write(self, s):
-        self.shell.write(s, self.tags)
+    @property
+    def encoding(self):
+        return self._encoding
 
-    def writelines(self, lines):
-        for line in lines:
-            self.write(line)
-
-    def flush(self):
-        pass
+    @property
+    def name(self):
+        return '<%s>' % self.tags
 
     def isatty(self):
         return True
 
 
+class PseudoOutputFile(PseudoFile):
+
+    def writable(self):
+        return True
+
+    def write(self, s):
+        if self.closed:
+            raise ValueError("write to closed file")
+        if not isinstance(s, (basestring, bytearray)):
+            raise TypeError('must be string, not ' + type(s).__name__)
+        return self.shell.write(s, self.tags)
+
+
+class PseudoInputFile(PseudoFile):
+
+    def __init__(self, shell, tags, encoding=None):
+        PseudoFile.__init__(self, shell, tags, encoding)
+        self._line_buffer = ''
+
+    def readable(self):
+        return True
+
+    def read(self, size=-1):
+        if self.closed:
+            raise ValueError("read from closed file")
+        if size is None:
+            size = -1
+        elif not isinstance(size, int):
+            raise TypeError('must be int, not ' + type(size).__name__)
+        result = self._line_buffer
+        self._line_buffer = ''
+        if size < 0:
+            while True:
+                line = self.shell.readline()
+                if not line: break
+                result += line
+        else:
+            while len(result) < size:
+                line = self.shell.readline()
+                if not line: break
+                result += line
+            self._line_buffer = result[size:]
+            result = result[:size]
+        return result
+
+    def readline(self, size=-1):
+        if self.closed:
+            raise ValueError("read from closed file")
+        if size is None:
+            size = -1
+        elif not isinstance(size, int):
+            raise TypeError('must be int, not ' + type(size).__name__)
+        line = self._line_buffer or self.shell.readline()
+        if size < 0:
+            size = len(line)
+        self._line_buffer = line[size:]
+        return line[:size]
+
+
 usage_msg = """\
 
 USAGE: idle  [-deins] [-t title] [file]*
@@ -1412,8 +1495,10 @@
 
     if enable_edit:
         if not (cmd or script):
-            for filename in args:
-                flist.open(filename)
+            for filename in args[:]:
+                if flist.open(filename) is None:
+                    # filename is a directory actually, disconsider it
+                    args.remove(filename)
             if not args:
                 flist.new()
     if enable_shell:
@@ -1456,7 +1541,8 @@
     if tkversionwarning:
         shell.interp.runcommand(''.join(("print('", tkversionwarning, "')")))
 
-    root.mainloop()
+    while flist.inversedict:  # keep IDLE running while files are open.
+        root.mainloop()
     root.destroy()
 
 if __name__ == "__main__":
diff --git a/lib-python/2.7/idlelib/ReplaceDialog.py b/lib-python/2.7/idlelib/ReplaceDialog.py
--- a/lib-python/2.7/idlelib/ReplaceDialog.py
+++ b/lib-python/2.7/idlelib/ReplaceDialog.py
@@ -2,6 +2,8 @@
 
 from idlelib import SearchEngine
 from idlelib.SearchDialogBase import SearchDialogBase
+import re
+
 
 def replace(text):
     root = text._root()
@@ -11,6 +13,7 @@
     dialog = engine._replacedialog
     dialog.open(text)
 
+
 class ReplaceDialog(SearchDialogBase):
 
     title = "Replace Dialog"
@@ -55,8 +58,22 @@
 
     def default_command(self, event=None):
         if self.do_find(self.ok):
-            self.do_replace()
-            self.do_find(0)
+            if self.do_replace():   # Only find next match if replace succeeded.
+                                    # A bad re can cause a it to fail.
+                self.do_find(0)
+
+    def _replace_expand(self, m, repl):
+        """ Helper function for expanding a regular expression
+            in the replace field, if needed. """
+        if self.engine.isre():
+            try:
+                new = m.expand(repl)
+            except re.error:
+                self.engine.report_error(repl, 'Invalid Replace Expression')
+                new = None
+        else:
+            new = repl
+        return new
 
     def replace_all(self, event=None):
         prog = self.engine.getprog()
@@ -86,7 +103,9 @@
             line, m = res
             chars = text.get("%d.0" % line, "%d.0" % (line+1))
             orig = m.group()
-            new = m.expand(repl)
+            new = self._replace_expand(m, repl)
+            if new is None:
+                break
             i, j = m.span()
             first = "%d.%d" % (line, i)
             last = "%d.%d" % (line, j)
@@ -103,7 +122,6 @@
         text.undo_block_stop()
         if first and last:
             self.show_hit(first, last)
-        self.close()
 
     def do_find(self, ok=0):
         if not self.engine.getprog():
@@ -138,7 +156,9 @@
         m = prog.match(chars, col)
         if not prog:
             return False
-        new = m.expand(self.replvar.get())
+        new = self._replace_expand(m, self.replvar.get())
+        if new is None:
+            return False
         text.mark_set("insert", first)
         text.undo_block_start()
         if m.group():
diff --git a/lib-python/2.7/idlelib/config-extensions.def b/lib-python/2.7/idlelib/config-extensions.def
--- a/lib-python/2.7/idlelib/config-extensions.def
+++ b/lib-python/2.7/idlelib/config-extensions.def
@@ -46,6 +46,8 @@
 
 [ScriptBinding]
 enable=1
+enable_shell=0
+enable_editor=1
 [ScriptBinding_cfgBindings]
 run-module=<Key-F5>
 check-module=<Alt-Key-x>
diff --git a/lib-python/2.7/idlelib/configDialog.py b/lib-python/2.7/idlelib/configDialog.py
--- a/lib-python/2.7/idlelib/configDialog.py
+++ b/lib-python/2.7/idlelib/configDialog.py
@@ -183,7 +183,7 @@
                               text=' Highlighting Theme ')
         #frameCustom
         self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
-            font=('courier',12,''),cursor='hand2',width=21,height=10,
+            font=('courier',12,''),cursor='hand2',width=21,height=11,
             takefocus=FALSE,highlightthickness=0,wrap=NONE)
         text=self.textHighlightSample
         text.bind('<Double-Button-1>',lambda e: 'break')
@@ -832,8 +832,9 @@
             fontWeight=tkFont.BOLD
         else:
             fontWeight=tkFont.NORMAL
-        self.editFont.config(size=self.fontSize.get(),
-                weight=fontWeight,family=fontName)
+        newFont = (fontName, self.fontSize.get(), fontWeight)
+        self.labelFontSample.config(font=newFont)
+        self.textHighlightSample.configure(font=newFont)
 
     def SetHighlightTarget(self):
         if self.highlightTarget.get()=='Cursor': #bg not possible
@@ -946,7 +947,7 @@
             self.listFontName.select_anchor(currentFontIndex)
         ##font size dropdown
         fontSize=idleConf.GetOption('main','EditorWindow','font-size',
-                default='10')
+                type='int', default='10')
         self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14',
                 '16','18','20','22'),fontSize )
         ##fontWeight
@@ -1032,10 +1033,13 @@
         self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave',
                                              default=0, type='bool'))
         #initial window size
-        self.winWidth.set(idleConf.GetOption('main','EditorWindow','width'))
-        self.winHeight.set(idleConf.GetOption('main','EditorWindow','height'))
+        self.winWidth.set(idleConf.GetOption('main','EditorWindow','width',
+                                             type='int'))
+        self.winHeight.set(idleConf.GetOption('main','EditorWindow','height',
+                                              type='int'))
         #initial paragraph reformat size
-        self.paraWidth.set(idleConf.GetOption('main','FormatParagraph','paragraph'))
+        self.paraWidth.set(idleConf.GetOption('main','FormatParagraph','paragraph',
+                                              type='int'))
         # default source encoding
         self.encoding.set(idleConf.GetOption('main', 'EditorWindow',
                                              'encoding', default='none'))
diff --git a/lib-python/2.7/idlelib/configHandler.py b/lib-python/2.7/idlelib/configHandler.py
--- a/lib-python/2.7/idlelib/configHandler.py
+++ b/lib-python/2.7/idlelib/configHandler.py
@@ -237,24 +237,39 @@
         printed to stderr.
 
         """
-        if self.userCfg[configType].has_option(section,option):
-            return self.userCfg[configType].Get(section, option,
-                                                type=type, raw=raw)
-        elif self.defaultCfg[configType].has_option(section,option):
-            return self.defaultCfg[configType].Get(section, option,
-                                                   type=type, raw=raw)
-        else: #returning default, print warning
-            if warn_on_default:
-                warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
-                           ' problem retrieving configuration option %r\n'
-                           ' from section %r.\n'
-                           ' returning default value: %r\n' %
-                           (option, section, default))
-                try:
-                    sys.stderr.write(warning)
-                except IOError:
-                    pass
-            return default
+        try:
+            if self.userCfg[configType].has_option(section,option):
+                return self.userCfg[configType].Get(section, option,
+                                                    type=type, raw=raw)
+        except ValueError:
+            warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
+                       ' invalid %r value for configuration option %r\n'
+                       ' from section %r: %r\n' %
+                       (type, option, section,
+                        self.userCfg[configType].Get(section, option,
+                                                     raw=raw)))
+            try:
+                sys.stderr.write(warning)
+            except IOError:
+                pass
+        try:
+            if self.defaultCfg[configType].has_option(section,option):
+                return self.defaultCfg[configType].Get(section, option,
+                                                       type=type, raw=raw)
+        except ValueError:
+            pass
+        #returning default, print warning
+        if warn_on_default:
+            warning = ('\n Warning: configHandler.py - IdleConf.GetOption -\n'
+                       ' problem retrieving configuration option %r\n'
+                       ' from section %r.\n'
+                       ' returning default value: %r\n' %
+                       (option, section, default))
+            try:
+                sys.stderr.write(warning)
+            except IOError:
+                pass
+        return default
 
     def SetOption(self, configType, section, option, value):
         """In user's config file, set section's option to value.
@@ -595,7 +610,7 @@
             '<<replace>>': ['<Control-h>'],
             '<<goto-line>>': ['<Alt-g>'],
             '<<smart-backspace>>': ['<Key-BackSpace>'],
-            '<<newline-and-indent>>': ['<Key-Return> <Key-KP_Enter>'],
+            '<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
             '<<smart-indent>>': ['<Key-Tab>'],
             '<<indent-region>>': ['<Control-Key-bracketright>'],
             '<<dedent-region>>': ['<Control-Key-bracketleft>'],
diff --git a/lib-python/2.7/idlelib/help.txt b/lib-python/2.7/idlelib/help.txt
--- a/lib-python/2.7/idlelib/help.txt
+++ b/lib-python/2.7/idlelib/help.txt
@@ -80,7 +80,7 @@
 Debug Menu (only in Shell window):
 
 	Go to File/Line   -- look around the insert point for a filename
-		             and linenumber, open the file, and show the line
+		             and line number, open the file, and show the line
 	Debugger (toggle) -- Run commands in the shell under the debugger
 	Stack Viewer      -- Show the stack traceback of the last exception
 	Auto-open Stack Viewer (toggle) -- Open stack viewer on traceback
@@ -92,7 +92,7 @@
                           Startup Preferences may be set, and Additional Help
                           Sources can be specified.
 			  
-			  On MacOS X this menu is not present, use
+			  On OS X this menu is not present, use
 			  menu 'IDLE -> Preferences...' instead.
 	---
 	Code Context --	  Open a pane at the top of the edit window which
@@ -120,6 +120,24 @@
 	---
 	(Additional Help Sources may be added here)
 
+Edit context menu (Right-click / Control-click on OS X in Edit window):
+
+    	Cut              -- Copy a selection into system-wide clipboard,
+                            then delete the selection
+	Copy             -- Copy selection into system-wide clipboard
+	Paste            -- Insert system-wide clipboard into window
+	Set Breakpoint   -- Sets a breakpoint (when debugger open)
+	Clear Breakpoint -- Clears the breakpoint on that line
+
+Shell context menu (Right-click / Control-click on OS X in Shell window):
+
+	Cut              -- Copy a selection into system-wide clipboard,
+                            then delete the selection
+	Copy             -- Copy selection into system-wide clipboard
+	Paste            -- Insert system-wide clipboard into window
+        ---
+	Go to file/line  -- Same as in Debug menu
+
 
 ** TIPS **
 ==========
@@ -222,7 +240,7 @@
 
 	Alt-p retrieves previous command matching what you have typed.
 	Alt-n retrieves next.
-	      (These are Control-p, Control-n on the Mac)
+	      (These are Control-p, Control-n on OS X)
 	Return while cursor is on a previous command retrieves that command.
 	Expand word is also useful to reduce typing.
 
diff --git a/lib-python/2.7/idlelib/idlever.py b/lib-python/2.7/idlelib/idlever.py
--- a/lib-python/2.7/idlelib/idlever.py
+++ b/lib-python/2.7/idlelib/idlever.py
@@ -1,1 +1,1 @@
-IDLE_VERSION = "2.7.3rc2"
+IDLE_VERSION = "2.7.3"
diff --git a/lib-python/2.7/idlelib/macosxSupport.py b/lib-python/2.7/idlelib/macosxSupport.py
--- a/lib-python/2.7/idlelib/macosxSupport.py
+++ b/lib-python/2.7/idlelib/macosxSupport.py
@@ -37,17 +37,21 @@
 def tkVersionWarning(root):
     """
     Returns a string warning message if the Tk version in use appears to
-    be one known to cause problems with IDLE.  The Apple Cocoa-based Tk 8.5
-    that was shipped with Mac OS X 10.6.
+    be one known to cause problems with IDLE.
+    1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
+    2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
+        can still crash unexpectedly.
     """
 
     if (runningAsOSXApp() and
-            ('AppKit' in root.tk.call('winfo', 'server', '.')) and
-            (root.tk.call('info', 'patchlevel') == '8.5.7') ):
-        return (r"WARNING: The version of Tcl/Tk (8.5.7) in use may"
+            ('AppKit' in root.tk.call('winfo', 'server', '.')) ):
+        patchlevel = root.tk.call('info', 'patchlevel')
+        if patchlevel not in ('8.5.7', '8.5.9'):
+            return False
+        return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
                 r" be unstable.\n"
                 r"Visit http://www.python.org/download/mac/tcltk/"
-                r" for current information.")
+                r" for current information.".format(patchlevel))
     else:
         return False
 
diff --git a/lib-python/2.7/idlelib/run.py b/lib-python/2.7/idlelib/run.py
--- a/lib-python/2.7/idlelib/run.py
+++ b/lib-python/2.7/idlelib/run.py
@@ -1,4 +1,5 @@
 import sys
+import io
 import linecache
 import time
 import socket
@@ -14,6 +15,8 @@
 from idlelib import RemoteObjectBrowser
 from idlelib import StackViewer
 from idlelib import rpc
+from idlelib import PyShell
+from idlelib import IOBinding
 
 import __main__
 
@@ -248,19 +251,19 @@
             quitting = True
             thread.interrupt_main()
 
-
 class MyHandler(rpc.RPCHandler):
 
     def handle(self):
         """Override base method"""
         executive = Executive(self)
         self.register("exec", executive)
-        sys.stdin = self.console = self.get_remote_proxy("stdin")
-        sys.stdout = self.get_remote_proxy("stdout")
-        sys.stderr = self.get_remote_proxy("stderr")
-        from idlelib import IOBinding
-        sys.stdin.encoding = sys.stdout.encoding = \
-                             sys.stderr.encoding = IOBinding.encoding
+        self.console = self.get_remote_proxy("console")
+        sys.stdin = PyShell.PseudoInputFile(self.console, "stdin",
+                IOBinding.encoding)
+        sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout",
+                IOBinding.encoding)
+        sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr",
+                IOBinding.encoding)
         self.interp = self.get_remote_proxy("interp")
         rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
 
@@ -298,11 +301,14 @@
                 exec code in self.locals
             finally:
                 interruptable = False
+        except SystemExit:
+            # Scripts that raise SystemExit should just
+            # return to the interactive prompt
+            pass
         except:
             self.usr_exc_info = sys.exc_info()
             if quitting:
                 exit()
-            # even print a user code SystemExit exception, continue
             print_exception()
             jit = self.rpchandler.console.getvar("<<toggle-jit-stack-viewer>>")
             if jit:
diff --git a/lib-python/2.7/io.py b/lib-python/2.7/io.py
--- a/lib-python/2.7/io.py
+++ b/lib-python/2.7/io.py
@@ -4,7 +4,7 @@
 At the top of the I/O hierarchy is the abstract base class IOBase. It
 defines the basic interface to a stream. Note, however, that there is no
 separation between reading and writing to streams; implementations are
-allowed to throw an IOError if they do not support a given operation.
+allowed to raise an IOError if they do not support a given operation.
 
 Extending IOBase is RawIOBase which deals simply with the reading and
 writing of raw bytes to a stream. FileIO subclasses RawIOBase to provide
@@ -34,15 +34,6 @@
 """
 # New I/O library conforming to PEP 3116.
 
-# XXX edge cases when switching between reading/writing
-# XXX need to support 1 meaning line-buffered
-# XXX whenever an argument is None, use the default value
-# XXX read/write ops should check readable/writable
-# XXX buffered readinto should work with arbitrary buffer objects
-# XXX use incremental encoder for text output, at least for UTF-16 and UTF-8-SIG
-# XXX check writable, readable and seekable in appropriate places
-
-
 __author__ = ("Guido van Rossum <guido at python.org>, "
               "Mike Verdone <mike.verdone at gmail.com>, "
               "Mark Russell <mark.russell at zen.co.uk>, "
diff --git a/lib-python/2.7/json/__init__.py b/lib-python/2.7/json/__init__.py
--- a/lib-python/2.7/json/__init__.py
+++ b/lib-python/2.7/json/__init__.py
@@ -37,8 +37,8 @@
 Pretty printing::
 
     >>> import json
-    >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4)
-    >>> print '\n'.join([l.rstrip() for l in  s.splitlines()])
+    >>> print json.dumps({'4': 5, '6': 7}, sort_keys=True,
+    ...                  indent=4, separators=(',', ': '))
     {
         "4": 5,
         "6": 7
@@ -95,7 +95,7 @@
         "json": "obj"
     }
     $ echo '{ 1.2:3.4}' | python -m json.tool
-    Expecting property name: line 1 column 2 (char 2)
+    Expecting property name enclosed in double quotes: line 1 column 3 (char 2)
 """
 __version__ = '2.0.9'
 __all__ = [
@@ -121,7 +121,7 @@
 
 def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
         allow_nan=True, cls=None, indent=None, separators=None,
-        encoding='utf-8', default=None, **kw):
+        encoding='utf-8', default=None, sort_keys=False, **kw):
     """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
     ``.write()``-supporting file-like object).
 
@@ -129,11 +129,14 @@
     (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
     will be skipped instead of raising a ``TypeError``.
 
-    If ``ensure_ascii`` is false, then the some chunks written to ``fp``
-    may be ``unicode`` instances, subject to normal Python ``str`` to
-    ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
-    understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
-    to cause an error.
+    If ``ensure_ascii`` is true (the default), all non-ASCII characters in the
+    output are escaped with ``\uXXXX`` sequences, and the result is a ``str``
+    instance consisting of ASCII characters only.  If ``ensure_ascii`` is
+    ``False``, some chunks written to ``fp`` may be ``unicode`` instances.
+    This usually happens because the input contains unicode strings or the
+    ``encoding`` parameter is used. Unless ``fp.write()`` explicitly
+    understands ``unicode`` (as in ``codecs.getwriter``) this is likely to
+    cause an error.
 
     If ``check_circular`` is false, then the circular reference check
     for container types will be skipped and a circular reference will
@@ -147,7 +150,9 @@
     If ``indent`` is a non-negative integer, then JSON array elements and
     object members will be pretty-printed with that indent level. An indent
     level of 0 will only insert newlines. ``None`` is the most compact
-    representation.
+    representation.  Since the default item separator is ``', '``,  the
+    output might include trailing whitespace when ``indent`` is specified.
+    You can use ``separators=(',', ': ')`` to avoid this.
 
     If ``separators`` is an ``(item_separator, dict_separator)`` tuple
     then it will be used instead of the default ``(', ', ': ')`` separators.
@@ -158,6 +163,9 @@
     ``default(obj)`` is a function that should return a serializable version
     of obj or raise TypeError. The default simply raises TypeError.
 
+    If *sort_keys* is ``True`` (default: ``False``), then the output of
+    dictionaries will be sorted by key.
+
     To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
     ``.default()`` method to serialize additional types), specify it with
     the ``cls`` kwarg; otherwise ``JSONEncoder`` is used.
@@ -167,7 +175,7 @@
     if (not skipkeys and ensure_ascii and
         check_circular and allow_nan and
         cls is None and indent is None and separators is None and
-        encoding == 'utf-8' and default is None and not kw):
+        encoding == 'utf-8' and default is None and not sort_keys and not kw):
         iterable = _default_encoder.iterencode(obj)
     else:
         if cls is None:
@@ -175,7 +183,7 @@
         iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
             check_circular=check_circular, allow_nan=allow_nan, indent=indent,
             separators=separators, encoding=encoding,
-            default=default, **kw).iterencode(obj)
+            default=default, sort_keys=sort_keys, **kw).iterencode(obj)
     # could accelerate with writelines in some versions of Python, at
     # a debuggability cost
     for chunk in iterable:
@@ -184,16 +192,15 @@
 
 def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
         allow_nan=True, cls=None, indent=None, separators=None,
-        encoding='utf-8', default=None, **kw):
+        encoding='utf-8', default=None, sort_keys=False, **kw):
     """Serialize ``obj`` to a JSON formatted ``str``.
 
     If ``skipkeys`` is false then ``dict`` keys that are not basic types
     (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
     will be skipped instead of raising a ``TypeError``.
 
-    If ``ensure_ascii`` is false, then the return value will be a
-    ``unicode`` instance subject to normal Python ``str`` to ``unicode``
-    coercion rules instead of being escaped to an ASCII ``str``.
+    If ``ensure_ascii`` is false, all non-ASCII characters are not escaped, and
+    the return value may be a ``unicode`` instance. See ``dump`` for details.
 
     If ``check_circular`` is false, then the circular reference check
     for container types will be skipped and a circular reference will
@@ -207,7 +214,9 @@
     If ``indent`` is a non-negative integer, then JSON array elements and
     object members will be pretty-printed with that indent level. An indent
     level of 0 will only insert newlines. ``None`` is the most compact
-    representation.
+    representation.  Since the default item separator is ``', '``,  the
+    output might include trailing whitespace when ``indent`` is specified.
+    You can use ``separators=(',', ': ')`` to avoid this.
 
     If ``separators`` is an ``(item_separator, dict_separator)`` tuple
     then it will be used instead of the default ``(', ', ': ')`` separators.
@@ -218,6 +227,9 @@
     ``default(obj)`` is a function that should return a serializable version
     of obj or raise TypeError. The default simply raises TypeError.
 
+    If *sort_keys* is ``True`` (default: ``False``), then the output of
+    dictionaries will be sorted by key.
+
     To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
     ``.default()`` method to serialize additional types), specify it with
     the ``cls`` kwarg; otherwise ``JSONEncoder`` is used.
@@ -227,7 +239,7 @@
     if (not skipkeys and ensure_ascii and
         check_circular and allow_nan and
         cls is None and indent is None and separators is None and
-        encoding == 'utf-8' and default is None and not kw):
+        encoding == 'utf-8' and default is None and not sort_keys and not kw):
         return _default_encoder.encode(obj)
     if cls is None:
         cls = JSONEncoder
@@ -235,7 +247,7 @@
         skipkeys=skipkeys, ensure_ascii=ensure_ascii,
         check_circular=check_circular, allow_nan=allow_nan, indent=indent,
         separators=separators, encoding=encoding, default=default,
-        **kw).encode(obj)
+        sort_keys=sort_keys, **kw).encode(obj)
 
 
 _default_decoder = JSONDecoder(encoding=None, object_hook=None,
diff --git a/lib-python/2.7/json/decoder.py b/lib-python/2.7/json/decoder.py
--- a/lib-python/2.7/json/decoder.py
+++ b/lib-python/2.7/json/decoder.py
@@ -27,7 +27,7 @@
 def linecol(doc, pos):
     lineno = doc.count('\n', 0, pos) + 1
     if lineno == 1:
-        colno = pos
+        colno = pos + 1
     else:
         colno = pos - doc.rindex('\n', 0, pos)
     return lineno, colno
@@ -169,7 +169,8 @@
                 pairs = object_hook(pairs)
             return pairs, end + 1
         elif nextchar != '"':
-            raise ValueError(errmsg("Expecting property name", s, end))
+            raise ValueError(errmsg(
+                "Expecting property name enclosed in double quotes", s, end))
     end += 1
     while True:
         key, end = scanstring(s, end, encoding, strict)
@@ -179,8 +180,7 @@
         if s[end:end + 1] != ':':
             end = _w(s, end).end()
             if s[end:end + 1] != ':':
-                raise ValueError(errmsg("Expecting : delimiter", s, end))
-
+                raise ValueError(errmsg("Expecting ':' delimiter", s, end))
         end += 1
 
         try:
@@ -209,7 +209,7 @@
         if nextchar == '}':
             break
         elif nextchar != ',':
-            raise ValueError(errmsg("Expecting , delimiter", s, end - 1))
+            raise ValueError(errmsg("Expecting ',' delimiter", s, end - 1))
 
         try:
             nextchar = s[end]
@@ -224,8 +224,8 @@
 
         end += 1
         if nextchar != '"':
-            raise ValueError(errmsg("Expecting property name", s, end - 1))
-
+            raise ValueError(errmsg(
+                "Expecting property name enclosed in double quotes", s, end - 1))
     if object_pairs_hook is not None:
         result = object_pairs_hook(pairs)
         return result, end
@@ -259,8 +259,7 @@
         if nextchar == ']':
             break
         elif nextchar != ',':
-            raise ValueError(errmsg("Expecting , delimiter", s, end))
-
+            raise ValueError(errmsg("Expecting ',' delimiter", s, end))
         try:
             if s[end] in _ws:
                 end += 1
diff --git a/lib-python/2.7/json/encoder.py b/lib-python/2.7/json/encoder.py
--- a/lib-python/2.7/json/encoder.py
+++ b/lib-python/2.7/json/encoder.py
@@ -27,8 +27,7 @@
     ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
     #ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
 
-# Assume this produces an infinity on all machines (probably not guaranteed)
-INFINITY = float('1e66666')
+INFINITY = float('inf')
 FLOAT_REPR = repr
 
 def encode_basestring(s):
@@ -108,9 +107,12 @@
         encoding of keys that are not str, int, long, float or None.  If
         skipkeys is True, such items are simply skipped.
 
-        If ensure_ascii is true, the output is guaranteed to be str
-        objects with all incoming unicode characters escaped.  If
-        ensure_ascii is false, the output will be unicode object.
+        If *ensure_ascii* is true (the default), all non-ASCII
+        characters in the output are escaped with \uXXXX sequences,
+        and the results are str instances consisting of ASCII
+        characters only.  If ensure_ascii is False, a result may be a
+        unicode instance.  This usually happens if the input contains
+        unicode strings or the *encoding* parameter is used.
 
         If check_circular is true, then lists, dicts, and custom encoded
         objects will be checked for circular references during encoding to
@@ -129,7 +131,10 @@
         If indent is a non-negative integer, then JSON array
         elements and object members will be pretty-printed with that
         indent level.  An indent level of 0 will only insert newlines.
-        None is the most compact representation.
+        None is the most compact representation.  Since the default
+        item separator is ', ',  the output might include trailing
+        whitespace when indent is specified.  You can use
+        separators=(',', ': ') to avoid this.
 
         If specified, separators should be a (item_separator, key_separator)
         tuple.  The default is (', ', ': ').  To get the most compact JSON
diff --git a/lib-python/2.7/json/tests/test_decode.py b/lib-python/2.7/json/tests/test_decode.py
--- a/lib-python/2.7/json/tests/test_decode.py
+++ b/lib-python/2.7/json/tests/test_decode.py
@@ -45,6 +45,15 @@
                                     object_hook=lambda x: None),
                          OrderedDict(p))
 
+    def test_extra_data(self):
+        s = '[1, 2, 3]5'
+        msg = 'Extra data'
+        self.assertRaisesRegexp(ValueError, msg, self.loads, s)
+
+    def test_invalid_escape(self):
+        s = '["abc\\y"]'
+        msg = 'escape'
+        self.assertRaisesRegexp(ValueError, msg, self.loads, s)
 
 class TestPyDecode(TestDecode, PyTest): pass
 class TestCDecode(TestDecode, CTest): pass
diff --git a/lib-python/2.7/json/tests/test_dump.py b/lib-python/2.7/json/tests/test_dump.py
--- a/lib-python/2.7/json/tests/test_dump.py
+++ b/lib-python/2.7/json/tests/test_dump.py
@@ -19,5 +19,14 @@
                 {2: 3.0, 4.0: 5L, False: 1, 6L: True}, sort_keys=True),
                 '{"false": 1, "2": 3.0, "4.0": 5, "6": true}')
 
+    # Issue 16228: Crash on encoding resized list
+    def test_encode_mutated(self):
+        a = [object()] * 10
+        def crasher(obj):
+            del a[-1]
+        self.assertEqual(self.dumps(a, default=crasher),
+                 '[null, null, null, null, null]')
+
+
 class TestPyDump(TestDump, PyTest): pass
 class TestCDump(TestDump, CTest): pass
diff --git a/lib-python/2.7/json/tests/test_fail.py b/lib-python/2.7/json/tests/test_fail.py
--- a/lib-python/2.7/json/tests/test_fail.py
+++ b/lib-python/2.7/json/tests/test_fail.py
@@ -1,13 +1,13 @@
 from json.tests import PyTest, CTest
 
-# Fri Dec 30 18:57:26 2005
+# 2007-10-05
 JSONDOCS = [
     # http://json.org/JSON_checker/test/fail1.json
     '"A JSON payload should be an object or array, not a string."',
     # http://json.org/JSON_checker/test/fail2.json
     '["Unclosed array"',
     # http://json.org/JSON_checker/test/fail3.json
-    '{unquoted_key: "keys must be quoted}',
+    '{unquoted_key: "keys must be quoted"}',
     # http://json.org/JSON_checker/test/fail4.json
     '["extra comma",]',
     # http://json.org/JSON_checker/test/fail5.json
@@ -33,7 +33,7 @@
     # http://json.org/JSON_checker/test/fail15.json
     '["Illegal backslash escape: \\x15"]',
     # http://json.org/JSON_checker/test/fail16.json
-    '["Illegal backslash escape: \\\'"]',
+    '[\\naked]',
     # http://json.org/JSON_checker/test/fail17.json
     '["Illegal backslash escape: \\017"]',
     # http://json.org/JSON_checker/test/fail18.json
@@ -50,6 +50,24 @@
     '["Bad value", truth]',
     # http://json.org/JSON_checker/test/fail24.json
     "['single quote']",
+    # http://json.org/JSON_checker/test/fail25.json
+    '["\ttab\tcharacter\tin\tstring\t"]',
+    # http://json.org/JSON_checker/test/fail26.json
+    '["tab\\   character\\   in\\  string\\  "]',
+    # http://json.org/JSON_checker/test/fail27.json
+    '["line\nbreak"]',
+    # http://json.org/JSON_checker/test/fail28.json
+    '["line\\\nbreak"]',
+    # http://json.org/JSON_checker/test/fail29.json
+    '[0e]',
+    # http://json.org/JSON_checker/test/fail30.json
+    '[0e+]',
+    # http://json.org/JSON_checker/test/fail31.json
+    '[0e+-1]',
+    # http://json.org/JSON_checker/test/fail32.json
+    '{"Comma instead if closing brace": true,',
+    # http://json.org/JSON_checker/test/fail33.json
+    '["mismatch"}',
     # http://code.google.com/p/simplejson/issues/detail?id=3
     u'["A\u001FZ control characters in string"]',
 ]
diff --git a/lib-python/2.7/json/tests/test_float.py b/lib-python/2.7/json/tests/test_float.py
--- a/lib-python/2.7/json/tests/test_float.py
+++ b/lib-python/2.7/json/tests/test_float.py
@@ -17,6 +17,21 @@
             self.assertEqual(self.loads(self.dumps(num)), num)
             self.assertEqual(self.loads(unicode(self.dumps(num))), num)
 
+    def test_out_of_range(self):
+        self.assertEqual(self.loads('[23456789012E666]'), [float('inf')])
+        self.assertEqual(self.loads('[-23456789012E666]'), [float('-inf')])
+
+    def test_allow_nan(self):
+        for val in (float('inf'), float('-inf'), float('nan')):
+            out = self.dumps([val])
+            if val == val:  # inf
+                self.assertEqual(self.loads(out), [val])
+            else:  # nan
+                res = self.loads(out)
+                self.assertEqual(len(res), 1)
+                self.assertNotEqual(res[0], res[0])
+            self.assertRaises(ValueError, self.dumps, [val], allow_nan=False)
+
 
 class TestPyFloat(TestFloat, PyTest): pass
 class TestCFloat(TestFloat, CTest): pass
diff --git a/lib-python/2.7/json/tests/test_pass1.py b/lib-python/2.7/json/tests/test_pass1.py
--- a/lib-python/2.7/json/tests/test_pass1.py
+++ b/lib-python/2.7/json/tests/test_pass1.py
@@ -17,7 +17,7 @@
         "real": -9876.543210,
         "e": 0.123456789e-12,
         "E": 1.234567890E+34,
-        "":  23456789012E666,
+        "":  23456789012E66,
         "zero": 0,
         "one": 1,
         "space": " ",
@@ -28,6 +28,7 @@
         "alpha": "abcdefghijklmnopqrstuvwyz",
         "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ",
         "digit": "0123456789",
+        "0123456789": "digit",
         "special": "`1~!@#$%^&*()_+-={':[,]}|;.</>?",
         "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A",
         "true": true,
@@ -43,8 +44,7 @@
 
 ,
 
-4 , 5        ,          6           ,7        ],
-        "compact": [1,2,3,4,5,6,7],
+4 , 5        ,          6           ,7        ],"compact":[1,2,3,4,5,6,7],
         "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}",
         "quotes": "" \u0022 %22 0x22 034 &#x22;",
         "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?"
@@ -55,9 +55,11 @@
 99.44
 ,
 
-1066
-
-
+1066,
+1e1,
+0.1e1,
+1e-1,
+1e00,2e+00,2e-00
 ,"rosebud"]
 '''
 
@@ -67,12 +69,6 @@
         res = self.loads(JSON)
         out = self.dumps(res)
         self.assertEqual(res, self.loads(out))
-        try:
-            self.dumps(res, allow_nan=False)
-        except ValueError:
-            pass
-        else:
-            self.fail("23456789012E666 should be out of range")
 
 
 class TestPyPass1(TestPass1, PyTest): pass
diff --git a/lib-python/2.7/json/tests/test_tool.py b/lib-python/2.7/json/tests/test_tool.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/json/tests/test_tool.py
@@ -0,0 +1,69 @@
+import os
+import sys
+import textwrap
+import unittest
+import subprocess
+from test import test_support
+from test.script_helper import assert_python_ok
+
+class TestTool(unittest.TestCase):
+    data = """
+
+        [["blorpie"],[ "whoops" ] , [
+                                 ],\t"d-shtaeou",\r"d-nthiouh",
+        "i-vhbjkhnth", {"nifty":87}, {"morefield" :\tfalse,"field"
+            :"yes"}  ]
+           """
+
+    expect = textwrap.dedent("""\
+    [
+        [
+            "blorpie"
+        ],
+        [
+            "whoops"
+        ],
+        [],
+        "d-shtaeou",
+        "d-nthiouh",
+        "i-vhbjkhnth",
+        {
+            "nifty": 87
+        },
+        {
+            "field": "yes",
+            "morefield": false
+        }
+    ]
+    """)
+
+    def test_stdin_stdout(self):
+        proc = subprocess.Popen(
+                (sys.executable, '-m', 'json.tool'),
+                stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+        out, err = proc.communicate(self.data.encode())
+        self.assertEqual(out.splitlines(), self.expect.encode().splitlines())
+        self.assertEqual(err, None)
+
+    def _create_infile(self):
+        infile = test_support.TESTFN
+        with open(infile, "w") as fp:
+            self.addCleanup(os.remove, infile)
+            fp.write(self.data)
+        return infile
+
+    def test_infile_stdout(self):
+        infile = self._create_infile()
+        rc, out, err = assert_python_ok('-m', 'json.tool', infile)
+        self.assertEqual(out.splitlines(), self.expect.encode().splitlines())
+        self.assertEqual(err, b'')
+
+    def test_infile_outfile(self):
+        infile = self._create_infile()
+        outfile = test_support.TESTFN + '.out'
+        rc, out, err = assert_python_ok('-m', 'json.tool', infile, outfile)
+        self.addCleanup(os.remove, outfile)
+        with open(outfile, "r") as fp:
+            self.assertEqual(fp.read(), self.expect)
+        self.assertEqual(out, b'')
+        self.assertEqual(err, b'')
diff --git a/lib-python/2.7/json/tool.py b/lib-python/2.7/json/tool.py
--- a/lib-python/2.7/json/tool.py
+++ b/lib-python/2.7/json/tool.py
@@ -7,7 +7,7 @@
         "json": "obj"
     }
     $ echo '{ 1.2:3.4}' | python -m json.tool
-    Expecting property name: line 1 column 2 (char 2)
+    Expecting property name enclosed in double quotes: line 1 column 3 (char 2)
 
 """
 import sys
@@ -25,12 +25,15 @@
         outfile = open(sys.argv[2], 'wb')
     else:
         raise SystemExit(sys.argv[0] + " [infile [outfile]]")
-    try:
-        obj = json.load(infile)
-    except ValueError, e:
-        raise SystemExit(e)
-    json.dump(obj, outfile, sort_keys=True, indent=4)
-    outfile.write('\n')
+    with infile:
+        try:
+            obj = json.load(infile)
+        except ValueError, e:
+            raise SystemExit(e)
+    with outfile:
+        json.dump(obj, outfile, sort_keys=True,
+                  indent=4, separators=(',', ': '))
+        outfile.write('\n')
 
 
 if __name__ == '__main__':
diff --git a/lib-python/2.7/lib-tk/Tkinter.py b/lib-python/2.7/lib-tk/Tkinter.py
--- a/lib-python/2.7/lib-tk/Tkinter.py
+++ b/lib-python/2.7/lib-tk/Tkinter.py
@@ -41,6 +41,7 @@
 TclError = _tkinter.TclError
 from types import *
 from Tkconstants import *
+import re
 
 wantobjects = 1
 
@@ -58,6 +59,37 @@
 except AttributeError: _tkinter.deletefilehandler = None
 
 
+_magic_re = re.compile(r'([\\{}])')
+_space_re = re.compile(r'([\s])')
+
+def _join(value):
+    """Internal function."""
+    return ' '.join(map(_stringify, value))
+
+def _stringify(value):
+    """Internal function."""
+    if isinstance(value, (list, tuple)):
+        if len(value) == 1:
+            value = _stringify(value[0])
+            if value[0] == '{':
+                value = '{%s}' % value
+        else:
+            value = '{%s}' % _join(value)
+    else:
+        if isinstance(value, basestring):
+            value = unicode(value)
+        else:
+            value = str(value)
+        if not value:
+            value = '{}'
+        elif _magic_re.search(value):
+            # add '\' before special characters and spaces
+            value = _magic_re.sub(r'\\\1', value)
+            value = _space_re.sub(r'\\\1', value)
+        elif value[0] == '"' or _space_re.search(value):
+            value = '{%s}' % value
+    return value
+
 def _flatten(tuple):
     """Internal function."""
     res = ()
@@ -154,8 +186,12 @@
     """Internal function."""
     pass
 
-def _exit(code='0'):
-    """Internal function. Calling it will throw the exception SystemExit."""
+def _exit(code=0):
+    """Internal function. Calling it will raise the exception SystemExit."""
+    try:
+        code = int(code)
+    except ValueError:
+        pass
     raise SystemExit, code
 
 _varnum = 0
@@ -534,12 +570,19 @@
 
         The type keyword specifies the form in which the data is
         to be returned and should be an atom name such as STRING
-        or FILE_NAME.  Type defaults to STRING.
+        or FILE_NAME.  Type defaults to STRING, except on X11, where the default
+        is to try UTF8_STRING and fall back to STRING.
 
         This command is equivalent to:
 
         selection_get(CLIPBOARD)
         """
+        if 'type' not in kw and self._windowingsystem == 'x11':
+            try:
+                kw['type'] = 'UTF8_STRING'
+                return self.tk.call(('clipboard', 'get') + self._options(kw))
+            except TclError:
+                del kw['type']
         return self.tk.call(('clipboard', 'get') + self._options(kw))
 
     def clipboard_clear(self, **kw):
@@ -621,8 +664,16 @@
         A keyword parameter selection specifies the name of
         the selection and defaults to PRIMARY.  A keyword
         parameter displayof specifies a widget on the display
-        to use."""
+        to use. A keyword parameter type specifies the form of data to be
+        fetched, defaulting to STRING except on X11, where UTF8_STRING is tried
+        before STRING."""
         if 'displayof' not in kw: kw['displayof'] = self._w
+        if 'type' not in kw and self._windowingsystem == 'x11':
+            try:
+                kw['type'] = 'UTF8_STRING'
+                return self.tk.call(('selection', 'get') + self._options(kw))
+            except TclError:
+                del kw['type']
         return self.tk.call(('selection', 'get') + self._options(kw))
     def selection_handle(self, command, **kw):
         """Specify a function COMMAND to call if the X
@@ -1037,6 +1088,15 @@
         if displayof is None:
             return ('-displayof', self._w)
         return ()
+    @property
+    def _windowingsystem(self):
+        """Internal function."""
+        try:
+            return self._root()._windowingsystem_cached
+        except AttributeError:
+            ws = self._root()._windowingsystem_cached = \
+                        self.tk.call('tk', 'windowingsystem')
+            return ws
     def _options(self, cnf, kw = None):
         """Internal function."""
         if kw:
@@ -1058,7 +1118,7 @@
                             nv.append('%d' % item)
                         else:
                             # format it to proper Tcl code if it contains space
-                            nv.append(('{%s}' if ' ' in item else '%s') % item)
+                            nv.append(_stringify(item))
                     else:
                         v = ' '.join(nv)
                 res = res + ('-'+k, v)
@@ -1685,7 +1745,9 @@
         self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
         if useTk:
             self._loadtk()
-        self.readprofile(baseName, className)
+        if not sys.flags.ignore_environment:
+            # Issue #16248: Honor the -E flag to avoid code injection.
+            self.readprofile(baseName, className)
     def loadtk(self):
         if not self._tkloaded:
             self.tk.loadtk()
diff --git a/lib-python/2.7/lib-tk/test/test_ttk/test_functions.py b/lib-python/2.7/lib-tk/test/test_ttk/test_functions.py
--- a/lib-python/2.7/lib-tk/test/test_ttk/test_functions.py
+++ b/lib-python/2.7/lib-tk/test/test_ttk/test_functions.py
@@ -50,13 +50,17 @@
             ttk._format_optdict({'test': {'left': 'as is'}}),
             {'-test': {'left': 'as is'}})
 
-        # check script formatting and untouched value(s)
+        # check script formatting
         check_against(
             ttk._format_optdict(
-                {'test': [1, -1, '', '2m', 0], 'nochange1': 3,
-                 'nochange2': 'abc def'}, script=True),
-            {'-test': '{1 -1 {} 2m 0}', '-nochange1': 3,
-             '-nochange2': 'abc def' })
+                {'test': [1, -1, '', '2m', 0], 'test2': 3,
+                 'test3': '', 'test4': 'abc def',
+                 'test5': '"abc"', 'test6': '{}',
+                 'test7': '} -spam {'}, script=True),
+            {'-test': '{1 -1 {} 2m 0}', '-test2': '3',
+             '-test3': '{}', '-test4': '{abc def}',
+             '-test5': '{"abc"}', '-test6': r'\{\}',
+             '-test7': r'\}\ -spam\ \{'})
 
         opts = {u'αβγ': True, u'á': False}
         orig_opts = opts.copy()
@@ -70,6 +74,32 @@
             ttk._format_optdict(
                 {'option': ('one two', 'three')}),
             {'-option': '{one two} three'})
+        check_against(
+            ttk._format_optdict(
+                {'option': ('one\ttwo', 'three')}),
+            {'-option': '{one\ttwo} three'})
+
+        # passing empty strings inside a tuple/list
+        check_against(
+            ttk._format_optdict(
+                {'option': ('', 'one')}),
+            {'-option': '{} one'})
+
+        # passing values with braces inside a tuple/list
+        check_against(
+            ttk._format_optdict(
+                {'option': ('one} {two', 'three')}),
+            {'-option': r'one\}\ \{two three'})
+
+        # passing quoted strings inside a tuple/list
+        check_against(
+            ttk._format_optdict(
+                {'option': ('"one"', 'two')}),
+            {'-option': '{"one"} two'})
+        check_against(
+            ttk._format_optdict(
+                {'option': ('{one}', 'two')}),
+            {'-option': r'\{one\} two'})
 
         # ignore an option
         amount_opts = len(ttk._format_optdict(opts, ignore=(u'á'))) // 2
diff --git a/lib-python/2.7/lib-tk/test/test_ttk/test_widgets.py b/lib-python/2.7/lib-tk/test/test_ttk/test_widgets.py
--- a/lib-python/2.7/lib-tk/test/test_ttk/test_widgets.py
+++ b/lib-python/2.7/lib-tk/test/test_ttk/test_widgets.py
@@ -188,6 +188,14 @@
         self.combo.configure(values=[1, '', 2])
         self.assertEqual(self.combo['values'], ('1', '', '2'))
 
+        # testing values with spaces
+        self.combo['values'] = ['a b', 'a\tb', 'a\nb']
+        self.assertEqual(self.combo['values'], ('a b', 'a\tb', 'a\nb'))
+
+        # testing values with special characters
+        self.combo['values'] = [r'a\tb', '"a"', '} {']
+        self.assertEqual(self.combo['values'], (r'a\tb', '"a"', '} {'))
+
         # out of range
         self.assertRaises(Tkinter.TclError, self.combo.current,
             len(self.combo['values']))
diff --git a/lib-python/2.7/lib-tk/tkSimpleDialog.py b/lib-python/2.7/lib-tk/tkSimpleDialog.py
--- a/lib-python/2.7/lib-tk/tkSimpleDialog.py
+++ b/lib-python/2.7/lib-tk/tkSimpleDialog.py
@@ -200,7 +200,7 @@
         self.entry = Entry(master, name="entry")
         self.entry.grid(row=1, padx=5, sticky=W+E)
 
-        if self.initialvalue:
+        if self.initialvalue is not None:
             self.entry.insert(0, self.initialvalue)
             self.entry.select_range(0, END)
 
diff --git a/lib-python/2.7/lib-tk/ttk.py b/lib-python/2.7/lib-tk/ttk.py
--- a/lib-python/2.7/lib-tk/ttk.py
+++ b/lib-python/2.7/lib-tk/ttk.py
@@ -26,8 +26,7 @@
            "tclobjs_to_py", "setup_master"]
 
 import Tkinter
-
-_flatten = Tkinter._flatten
+from Tkinter import _flatten, _join, _stringify
 
 # Verify if Tk is new enough to not need the Tile package
 _REQUIRE_TILE = True if Tkinter.TkVersion < 8.5 else False
@@ -47,39 +46,56 @@
         master.tk.eval('package require tile') # TclError may be raised here
         master._tile_loaded = True
 
+def _format_optvalue(value, script=False):
+    """Internal function."""
+    if script:
+        # if caller passes a Tcl script to tk.call, all the values need to
+        # be grouped into words (arguments to a command in Tcl dialect)
+        value = _stringify(value)
+    elif isinstance(value, (list, tuple)):
+        value = _join(value)
+    return value
+
 def _format_optdict(optdict, script=False, ignore=None):
     """Formats optdict to a tuple to pass it to tk.call.
 
     E.g. (script=False):
       {'foreground': 'blue', 'padding': [1, 2, 3, 4]} returns:
       ('-foreground', 'blue', '-padding', '1 2 3 4')"""
-    format = "%s" if not script else "{%s}"
 
     opts = []
     for opt, value in optdict.iteritems():
-        if ignore and opt in ignore:
-            continue
+        if not ignore or opt not in ignore:
+            opts.append("-%s" % opt)
+            if value is not None:
+                opts.append(_format_optvalue(value, script))
 
-        if isinstance(value, (list, tuple)):
-            v = []
-            for val in value:
-                if isinstance(val, basestring):
-                    v.append(unicode(val) if val else '{}')
-                else:
-                    v.append(str(val))
+    return _flatten(opts)
 
-            # format v according to the script option, but also check for
-            # space in any value in v in order to group them correctly
-            value = format % ' '.join(
-                ('{%s}' if ' ' in val else '%s') % val for val in v)
-
-        if script and value == '':
-            value = '{}' # empty string in Python is equivalent to {} in Tcl
-
-        opts.append(("-%s" % opt, value))
-
-    # Remember: _flatten skips over None
-    return _flatten(opts)
+def _mapdict_values(items):
+    # each value in mapdict is expected to be a sequence, where each item
+    # is another sequence containing a state (or several) and a value
+    # E.g. (script=False):
+    #   [('active', 'selected', 'grey'), ('focus', [1, 2, 3, 4])]
+    #   returns:
+    #   ['active selected', 'grey', 'focus', [1, 2, 3, 4]]
+    opt_val = []
+    for item in items:
+        state = item[:-1]
+        val = item[-1]
+        # hacks for bakward compatibility
+        state[0] # raise IndexError if empty
+        if len(state) == 1:
+            # if it is empty (something that evaluates to False), then
+            # format it to Tcl code to denote the "normal" state
+            state = state[0] or ''
+        else:
+            # group multiple states
+            state = ' '.join(state) # raise TypeError if not str
+        opt_val.append(state)
+        if val is not None:
+            opt_val.append(val)
+    return opt_val
 
 def _format_mapdict(mapdict, script=False):
     """Formats mapdict to pass it to tk.call.
@@ -90,32 +106,11 @@
       returns:
 
       ('-expand', '{active selected} grey focus {1, 2, 3, 4}')"""
-    # if caller passes a Tcl script to tk.call, all the values need to
-    # be grouped into words (arguments to a command in Tcl dialect)
-    format = "%s" if not script else "{%s}"
 
     opts = []
     for opt, value in mapdict.iteritems():
-
-        opt_val = []
-        # each value in mapdict is expected to be a sequence, where each item
-        # is another sequence containing a state (or several) and a value
-        for statespec in value:
-            state, val = statespec[:-1], statespec[-1]
-
-            if len(state) > 1: # group multiple states
-                state = "{%s}" % ' '.join(state)
-            else: # single state
-                # if it is empty (something that evaluates to False), then
-                # format it to Tcl code to denote the "normal" state
-                state = state[0] or '{}'
-
-            if isinstance(val, (list, tuple)): # val needs to be grouped
-                val = "{%s}" % ' '.join(map(str, val))
-
-            opt_val.append("%s %s" % (state, val))
-
-        opts.append(("-%s" % opt, format % ' '.join(opt_val)))
+        opts.extend(("-%s" % opt,
+                     _format_optvalue(_mapdict_values(value), script)))
 
     return _flatten(opts)
 
@@ -129,7 +124,7 @@
             iname = args[0]
             # next args, if any, are statespec/value pairs which is almost
             # a mapdict, but we just need the value
-            imagespec = _format_mapdict({None: args[1:]})[1]
+            imagespec = _join(_mapdict_values(args[1:]))
             spec = "%s %s" % (iname, imagespec)
 
         else:
@@ -138,7 +133,7 @@
             # themed styles on Windows XP and Vista.
             # Availability: Tk 8.6, Windows XP and Vista.
             class_name, part_id = args[:2]
-            statemap = _format_mapdict({None: args[2:]})[1]
+            statemap = _join(_mapdict_values(args[2:]))
             spec = "%s %s %s" % (class_name, part_id, statemap)
 
         opts = _format_optdict(kw, script)
@@ -148,11 +143,11 @@
         # otherwise it will clone {} (empty element)
         spec = args[0] # theme name
         if len(args) > 1: # elementfrom specified
-            opts = (args[1], )
+            opts = (_format_optvalue(args[1], script),)
 
     if script:
         spec = '{%s}' % spec
-        opts = ' '.join(map(str, opts))
+        opts = ' '.join(opts)
 
     return spec, opts
 
@@ -189,7 +184,7 @@
     for layout_elem in layout:
         elem, opts = layout_elem
         opts = opts or {}
-        fopts = ' '.join(map(str, _format_optdict(opts, True, "children")))
+        fopts = ' '.join(_format_optdict(opts, True, ("children",)))
         head = "%s%s%s" % (' ' * indent, elem, (" %s" % fopts) if fopts else '')
 
         if "children" in opts:
@@ -215,11 +210,11 @@
     for name, opts in settings.iteritems():
         # will format specific keys according to Tcl code
         if opts.get('configure'): # format 'configure'
-            s = ' '.join(map(unicode, _format_optdict(opts['configure'], True)))
+            s = ' '.join(_format_optdict(opts['configure'], True))
             script.append("ttk::style configure %s %s;" % (name, s))
 
         if opts.get('map'): # format 'map'
-            s = ' '.join(map(unicode, _format_mapdict(opts['map'], True)))
+            s = ' '.join(_format_mapdict(opts['map'], True))
             script.append("ttk::style map %s %s;" % (name, s))
 
         if 'layout' in opts: # format 'layout' which may be empty
@@ -706,30 +701,9 @@
             exportselection, justify, height, postcommand, state,
             textvariable, values, width
         """
-        # The "values" option may need special formatting, so leave to
-        # _format_optdict the responsibility to format it
-        if "values" in kw:
-            kw["values"] = _format_optdict({'v': kw["values"]})[1]
-
         Entry.__init__(self, master, "ttk::combobox", **kw)
 
 
-    def __setitem__(self, item, value):
-        if item == "values":
-            value = _format_optdict({item: value})[1]
-
-        Entry.__setitem__(self, item, value)
-
-
-    def configure(self, cnf=None, **kw):
-        """Custom Combobox configure, created to properly format the values
-        option."""
-        if "values" in kw:
-            kw["values"] = _format_optdict({'v': kw["values"]})[1]
-
-        return Entry.configure(self, cnf, **kw)
-
-
     def current(self, newindex=None):
         """If newindex is supplied, sets the combobox value to the
         element at position newindex in the list of values. Otherwise,
@@ -1253,7 +1227,7 @@
 
 
     def exists(self, item):
-        """Returns True if the specified item is present in the three,
+        """Returns True if the specified item is present in the tree,
         False otherwise."""
         return bool(self.tk.call(self._w, "exists", item))
 
diff --git a/lib-python/2.7/lib2to3/fixer_util.py b/lib-python/2.7/lib2to3/fixer_util.py
--- a/lib-python/2.7/lib2to3/fixer_util.py
+++ b/lib-python/2.7/lib2to3/fixer_util.py
@@ -165,7 +165,7 @@
 
 
 consuming_calls = set(["sorted", "list", "set", "any", "all", "tuple", "sum",
-                       "min", "max"])
+                       "min", "max", "enumerate"])
 
 def attr_chain(obj, attr):
     """Follow an attribute chain.
@@ -192,14 +192,14 @@
 p1 = """
 power<
     ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' |
-      'any' | 'all' | (any* trailer< '.' 'join' >) )
+      'any' | 'all' | 'enumerate' | (any* trailer< '.' 'join' >) )
     trailer< '(' node=any ')' >
     any*
 >
 """
 p2 = """
 power<
-    'sorted'
+    ( 'sorted' | 'enumerate' )
     trailer< '(' arglist<node=any any*> ')' >
     any*
 >
@@ -207,14 +207,14 @@
 pats_built = False
 def in_special_context(node):
     """ Returns true if node is in an environment where all that is required
-        of it is being itterable (ie, it doesn't matter if it returns a list
-        or an itterator).
+        of it is being iterable (ie, it doesn't matter if it returns a list
+        or an iterator).
         See test_map_nochange in test_fixers.py for some examples and tests.
         """
     global p0, p1, p2, pats_built
     if not pats_built:
+        p0 = patcomp.compile_pattern(p0)
         p1 = patcomp.compile_pattern(p1)
-        p0 = patcomp.compile_pattern(p0)
         p2 = patcomp.compile_pattern(p2)
         pats_built = True
     patterns = [p0, p1, p2]
@@ -274,9 +274,9 @@
     """Find the top level namespace."""
     # Scamper up to the top level namespace
     while node.type != syms.file_input:
-        assert node.parent, "Tree is insane! root found before "\
-                           "file_input node was found."
         node = node.parent
+        if not node:
+            raise ValueError("root found before file_input node was found.")
     return node
 
 def does_tree_import(package, name, node):
diff --git a/lib-python/2.7/lib2to3/pgen2/driver.py b/lib-python/2.7/lib2to3/pgen2/driver.py
--- a/lib-python/2.7/lib2to3/pgen2/driver.py
+++ b/lib-python/2.7/lib2to3/pgen2/driver.py
@@ -138,3 +138,20 @@
     if not os.path.exists(b):
         return True
     return os.path.getmtime(a) >= os.path.getmtime(b)
+
+
+def main(*args):
+    """Main program, when run as a script: produce grammar pickle files.
+
+    Calls load_grammar for each argument, a path to a grammar text file.
+    """
+    if not args:
+        args = sys.argv[1:]
+    logging.basicConfig(level=logging.INFO, stream=sys.stdout,
+                        format='%(message)s')
+    for gt in args:
+        load_grammar(gt, save=True, force=True)
+    return True
+
+if __name__ == "__main__":
+    sys.exit(int(not main()))
diff --git a/lib-python/2.7/lib2to3/refactor.py b/lib-python/2.7/lib2to3/refactor.py
--- a/lib-python/2.7/lib2to3/refactor.py
+++ b/lib-python/2.7/lib2to3/refactor.py
@@ -445,7 +445,7 @@
 
                         try:
                             find_root(node)
-                        except AssertionError:
+                        except ValueError:
                             # this node has been cut off from a
                             # previous transformation ; skip
                             continue
diff --git a/lib-python/2.7/lib2to3/tests/test_fixers.py b/lib-python/2.7/lib2to3/tests/test_fixers.py
--- a/lib-python/2.7/lib2to3/tests/test_fixers.py
+++ b/lib-python/2.7/lib2to3/tests/test_fixers.py
@@ -2981,6 +2981,10 @@
         self.unchanged(a)
         a = """sorted(filter(f, 'abc'), key=blah)[0]"""
         self.unchanged(a)
+        a = """enumerate(filter(f, 'abc'))"""
+        self.unchanged(a)
+        a = """enumerate(filter(f, 'abc'), start=1)"""
+        self.unchanged(a)
         a = """for i in filter(f, 'abc'): pass"""
         self.unchanged(a)
         a = """[x for x in filter(f, 'abc')]"""
@@ -3089,6 +3093,10 @@
         self.unchanged(a)
         a = """sorted(map(f, 'abc'), key=blah)[0]"""
         self.unchanged(a)
+        a = """enumerate(map(f, 'abc'))"""
+        self.unchanged(a)
+        a = """enumerate(map(f, 'abc'), start=1)"""
+        self.unchanged(a)
         a = """for i in map(f, 'abc'): pass"""
         self.unchanged(a)
         a = """[x for x in map(f, 'abc')]"""
@@ -3152,6 +3160,10 @@
         self.unchanged(a)
         a = """sorted(zip(a, b), key=blah)[0]"""
         self.unchanged(a)
+        a = """enumerate(zip(a, b))"""
+        self.unchanged(a)
+        a = """enumerate(zip(a, b), start=1)"""
+        self.unchanged(a)
         a = """for i in zip(a, b): pass"""
         self.unchanged(a)
         a = """[x for x in zip(a, b)]"""
diff --git a/lib-python/2.7/locale.py b/lib-python/2.7/locale.py
--- a/lib-python/2.7/locale.py
+++ b/lib-python/2.7/locale.py
@@ -18,6 +18,14 @@
 import operator
 import functools
 
+try:
+    _unicode = unicode
+except NameError:
+    # If Python is built without Unicode support, the unicode type
+    # will not exist. Fake one.
+    class _unicode(object):
+        pass
+
 # Try importing the _locale module.
 #
 # If this fails, fall back on a basic 'C' locale emulation.
@@ -353,7 +361,7 @@
 
     """
     # Normalize the locale name and extract the encoding
-    if isinstance(localename, unicode):
+    if isinstance(localename, _unicode):
         localename = localename.encode('ascii')
     fullname = localename.translate(_ascii_lower_map)
     if ':' in fullname:
diff --git a/lib-python/2.7/logging/__init__.py b/lib-python/2.7/logging/__init__.py
--- a/lib-python/2.7/logging/__init__.py
+++ b/lib-python/2.7/logging/__init__.py
@@ -180,7 +180,7 @@
         _releaseLock()
 
 def _checkLevel(level):
-    if isinstance(level, int):
+    if isinstance(level, (int, long)):
         rv = level
     elif str(level) == level:
         if level not in _levelNames:
@@ -624,7 +624,8 @@
     # This function can be called during module teardown, when globals are
     # set to None. If _acquireLock is None, assume this is the case and do
     # nothing.
-    if _acquireLock is not None:
+    if (_acquireLock is not None and _handlerList is not None and
+        _releaseLock is not None):
         _acquireLock()
         try:
             if wr in _handlerList:
@@ -1173,11 +1174,12 @@
         if self.isEnabledFor(ERROR):
             self._log(ERROR, msg, args, **kwargs)
 
-    def exception(self, msg, *args):
+    def exception(self, msg, *args, **kwargs):
         """
         Convenience method for logging an ERROR with exception information.
         """
-        self.error(msg, exc_info=1, *args)
+        kwargs['exc_info'] = 1
+        self.error(msg, *args, **kwargs)
 
     def critical(self, msg, *args, **kwargs):
         """
@@ -1250,7 +1252,7 @@
         all the handlers of this logger to handle the record.
         """
         if _srcfile:
-            #IronPython doesn't track Python frames, so findCaller throws an
+            #IronPython doesn't track Python frames, so findCaller raises an
             #exception on some versions of IronPython. We trap it here so that
             #IronPython can use logging.
             try:
@@ -1582,12 +1584,13 @@
         basicConfig()
     root.error(msg, *args, **kwargs)
 
-def exception(msg, *args):
+def exception(msg, *args, **kwargs):
     """
     Log a message with severity 'ERROR' on the root logger,
     with exception information.
     """
-    error(msg, exc_info=1, *args)
+    kwargs['exc_info'] = 1
+    error(msg, *args, **kwargs)
 
 def warning(msg, *args, **kwargs):
     """
diff --git a/lib-python/2.7/logging/handlers.py b/lib-python/2.7/logging/handlers.py
--- a/lib-python/2.7/logging/handlers.py
+++ b/lib-python/2.7/logging/handlers.py
@@ -23,7 +23,7 @@
 To use, simply 'import logging.handlers' and log away!
 """
 
-import logging, socket, os, cPickle, struct, time, re
+import errno, logging, socket, os, cPickle, struct, time, re
 from stat import ST_DEV, ST_INO, ST_MTIME
 
 try:
@@ -139,7 +139,6 @@
                 os.remove(dfn)
             os.rename(self.baseFilename, dfn)
             #print "%s -> %s" % (self.baseFilename, dfn)
-        self.mode = 'w'
         self.stream = self._open()
 
     def shouldRollover(self, record):
@@ -354,7 +353,6 @@
             for s in self.getFilesToDelete():
                 os.remove(s)
         #print "%s -> %s" % (self.baseFilename, dfn)
-        self.mode = 'w'
         self.stream = self._open()
         newRolloverAt = self.computeRollover(currentTime)
         while newRolloverAt <= currentTime:
@@ -392,11 +390,13 @@
     """
     def __init__(self, filename, mode='a', encoding=None, delay=0):
         logging.FileHandler.__init__(self, filename, mode, encoding, delay)
-        if not os.path.exists(self.baseFilename):
-            self.dev, self.ino = -1, -1
-        else:
-            stat = os.stat(self.baseFilename)
-            self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
+        self.dev, self.ino = -1, -1
+        self._statstream()
+
+    def _statstream(self):
+        if self.stream:
+            sres = os.fstat(self.stream.fileno())
+            self.dev, self.ino = sres[ST_DEV], sres[ST_INO]
 
     def emit(self, record):
         """
@@ -406,19 +406,27 @@
         has, close the old stream and reopen the file to get the
         current stream.
         """
-        if not os.path.exists(self.baseFilename):
-            stat = None
-            changed = 1
-        else:
-            stat = os.stat(self.baseFilename)
-            changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino)
-        if changed and self.stream is not None:
-            self.stream.flush()
-            self.stream.close()
-            self.stream = self._open()
-            if stat is None:
-                stat = os.stat(self.baseFilename)
-            self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
+        # Reduce the chance of race conditions by stat'ing by path only
+        # once and then fstat'ing our new fd if we opened a new log stream.
+        # See issue #14632: Thanks to John Mulligan for the problem report
+        # and patch.
+        try:
+            # stat the file by path, checking for existence
+            sres = os.stat(self.baseFilename)
+        except OSError as err:
+            if err.errno == errno.ENOENT:
+                sres = None
+            else:
+                raise
+        # compare file system stat with that of our stream file handle
+        if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
+            if self.stream is not None:
+                # we have an open file handle, clean it up
+                self.stream.flush()
+                self.stream.close()
+                # open a new file handle and get new stat info from that fd
+                self.stream = self._open()
+                self._statstream()
         logging.FileHandler.emit(self, record)
 
 class SocketHandler(logging.Handler):
@@ -528,9 +536,16 @@
         """
         ei = record.exc_info
         if ei:
-            dummy = self.format(record) # just to get traceback text into record.exc_text
+            # just to get traceback text into record.exc_text ...
+            dummy = self.format(record)
             record.exc_info = None  # to avoid Unpickleable error
-        s = cPickle.dumps(record.__dict__, 1)
+        # See issue #14436: If msg or args are objects, they may not be
+        # available on the receiving end. So we convert the msg % args
+        # to a string, save it as msg and zap the args.
+        d = dict(record.__dict__)
+        d['msg'] = record.getMessage()
+        d['args'] = None
+        s = cPickle.dumps(d, 1)
         if ei:
             record.exc_info = ei  # for next handler
         slen = struct.pack(">L", len(s))
@@ -747,14 +762,12 @@
         self.formatter = None
 
     def _connect_unixsocket(self, address):
-        self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
-        # syslog may require either DGRAM or STREAM sockets
+        self.socket = socket.socket(socket.AF_UNIX, self.socktype)
         try:
             self.socket.connect(address)
         except socket.error:
             self.socket.close()
-            self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            self.socket.connect(address)
+            raise
 
     # curious: when talking to the unix-domain '/dev/log' socket, a
     #   zero-terminator seems to be required.  this string is placed
@@ -814,8 +827,6 @@
         # Message is a string. Convert to bytes as required by RFC 5424
         if type(msg) is unicode:
             msg = msg.encode('utf-8')
-            if codecs:
-                msg = codecs.BOM_UTF8 + msg
         msg = prio + msg
         try:
             if self.unixsocket:
@@ -868,6 +879,7 @@
         self.toaddrs = toaddrs
         self.subject = subject
         self.secure = secure
+        self._timeout = 5.0
 
     def getSubject(self, record):
         """
@@ -890,7 +902,7 @@
             port = self.mailport
             if not port:
                 port = smtplib.SMTP_PORT
-            smtp = smtplib.SMTP(self.mailhost, port)
+            smtp = smtplib.SMTP(self.mailhost, port, timeout=self._timeout)
             msg = self.format(record)
             msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
                             self.fromaddr,
diff --git a/lib-python/2.7/mailbox.py b/lib-python/2.7/mailbox.py
--- a/lib-python/2.7/mailbox.py
+++ b/lib-python/2.7/mailbox.py
@@ -197,6 +197,9 @@
         """Flush and close the mailbox."""
         raise NotImplementedError('Method must be implemented by subclass')
 
+    # Whether each message must end in a newline
+    _append_newline = False
+
     def _dump_message(self, message, target, mangle_from_=False):
         # Most files are opened in binary mode to allow predictable seeking.
         # To get native line endings on disk, the user-friendly \n line endings
@@ -207,13 +210,21 @@
             gen = email.generator.Generator(buffer, mangle_from_, 0)
             gen.flatten(message)
             buffer.seek(0)
-            target.write(buffer.read().replace('\n', os.linesep))
+            data = buffer.read().replace('\n', os.linesep)
+            target.write(data)
+            if self._append_newline and not data.endswith(os.linesep):
+                # Make sure the message ends with a newline
+                target.write(os.linesep)
         elif isinstance(message, str):
             if mangle_from_:
                 message = message.replace('\nFrom ', '\n>From ')
             message = message.replace('\n', os.linesep)
             target.write(message)
+            if self._append_newline and not message.endswith(os.linesep):
+                # Make sure the message ends with a newline
+                target.write(os.linesep)
         elif hasattr(message, 'read'):
+            lastline = None
             while True:
                 line = message.readline()
                 if line == '':
@@ -222,6 +233,10 @@
                     line = '>From ' + line[5:]
                 line = line.replace('\n', os.linesep)
                 target.write(line)
+                lastline = line
+            if self._append_newline and lastline and not lastline.endswith(os.linesep):
+                # Make sure the message ends with a newline
+                target.write(os.linesep)
         else:
             raise TypeError('Invalid message type: %s' % type(message))
 
@@ -561,16 +576,19 @@
         self._file = f
         self._toc = None
         self._next_key = 0
-        self._pending = False   # No changes require rewriting the file.
+        self._pending = False       # No changes require rewriting the file.
+        self._pending_sync = False  # No need to sync the file
         self._locked = False
-        self._file_length = None        # Used to record mailbox size
+        self._file_length = None    # Used to record mailbox size
 
     def add(self, message):
         """Add message and return assigned key."""
         self._lookup()
         self._toc[self._next_key] = self._append_message(message)
         self._next_key += 1
-        self._pending = True
+        # _append_message appends the message to the mailbox file. We
+        # don't need a full rewrite + rename, sync is enough.
+        self._pending_sync = True
         return self._next_key - 1
 
     def remove(self, key):
@@ -616,6 +634,11 @@
     def flush(self):
         """Write any pending changes to disk."""
         if not self._pending:
+            if self._pending_sync:
+                # Messages have only been added, so syncing the file
+                # is enough.
+                _sync_flush(self._file)
+                self._pending_sync = False
             return
 
         # In order to be writing anything out at all, self._toc must
@@ -649,6 +672,7 @@
                     new_file.write(buffer)
                 new_toc[key] = (new_start, new_file.tell())
                 self._post_message_hook(new_file)
+            self._file_length = new_file.tell()
         except:
             new_file.close()
             os.remove(new_file.name)
@@ -656,6 +680,9 @@
         _sync_close(new_file)
         # self._file is about to get replaced, so no need to sync.
         self._file.close()
+        # Make sure the new file's mode is the same as the old file's
+        mode = os.stat(self._path).st_mode
+        os.chmod(new_file.name, mode)
         try:
             os.rename(new_file.name, self._path)
         except OSError, e:
@@ -668,6 +695,7 @@
         self._file = open(self._path, 'rb+')
         self._toc = new_toc
         self._pending = False
+        self._pending_sync = False
         if self._locked:
             _lock_file(self._file, dotlock=False)
 
@@ -704,6 +732,12 @@
         """Append message to mailbox and return (start, stop) offsets."""
         self._file.seek(0, 2)
         before = self._file.tell()
+        if len(self._toc) == 0 and not self._pending:
+            # This is the first message, and the _pre_mailbox_hook
+            # hasn't yet been called. If self._pending is True,
+            # messages have been removed, so _pre_mailbox_hook must
+            # have been called already.
+            self._pre_mailbox_hook(self._file)
         try:
             self._pre_message_hook(self._file)
             offsets = self._install_message(message)
@@ -778,30 +812,48 @@
 
     _mangle_from_ = True
 
+    # All messages must end in a newline character, and
+    # _post_message_hooks outputs an empty line between messages.
+    _append_newline = True
+
     def __init__(self, path, factory=None, create=True):
         """Initialize an mbox mailbox."""
         self._message_factory = mboxMessage
         _mboxMMDF.__init__(self, path, factory, create)
 
-    def _pre_message_hook(self, f):
-        """Called before writing each message to file f."""
-        if f.tell() != 0:
-            f.write(os.linesep)
+    def _post_message_hook(self, f):
+        """Called after writing each message to file f."""
+        f.write(os.linesep)
 
     def _generate_toc(self):
         """Generate key-to-(start, stop) table of contents."""
         starts, stops = [], []
+        last_was_empty = False
         self._file.seek(0)
         while True:
             line_pos = self._file.tell()
             line = self._file.readline()
             if line.startswith('From '):
                 if len(stops) < len(starts):
+                    if last_was_empty:
+                        stops.append(line_pos - len(os.linesep))
+                    else:
+                        # The last line before the "From " line wasn't
+                        # blank, but we consider it a start of a
+                        # message anyway.
+                        stops.append(line_pos)
+                starts.append(line_pos)
+                last_was_empty = False
+            elif not line:
+                if last_was_empty:
                     stops.append(line_pos - len(os.linesep))
-                starts.append(line_pos)
-            elif line == '':
-                stops.append(line_pos)
+                else:
+                    stops.append(line_pos)
                 break
+            elif line == os.linesep:
+                last_was_empty = True
+            else:
+                last_was_empty = False
         self._toc = dict(enumerate(zip(starts, stops)))
         self._next_key = len(self._toc)
         self._file_length = self._file.tell()
@@ -1367,9 +1419,9 @@
                 line = message.readline()
                 self._file.write(line.replace('\n', os.linesep))
                 if line == '\n' or line == '':
-                    self._file.write('*** EOOH ***' + os.linesep)
                     if first_pass:
                         first_pass = False
+                        self._file.write('*** EOOH ***' + os.linesep)
                         message.seek(original_pos)
                     else:
                         break
diff --git a/lib-python/2.7/mimetypes.py b/lib-python/2.7/mimetypes.py
--- a/lib-python/2.7/mimetypes.py
+++ b/lib-python/2.7/mimetypes.py
@@ -432,11 +432,12 @@
         '.hdf'    : 'application/x-hdf',
         '.htm'    : 'text/html',
         '.html'   : 'text/html',
+        '.ico'    : 'image/vnd.microsoft.icon',
         '.ief'    : 'image/ief',
         '.jpe'    : 'image/jpeg',
         '.jpeg'   : 'image/jpeg',
         '.jpg'    : 'image/jpeg',
-        '.js'     : 'application/x-javascript',
+        '.js'     : 'application/javascript',
         '.ksh'    : 'text/plain',
         '.latex'  : 'application/x-latex',
         '.m1v'    : 'video/mpeg',
diff --git a/lib-python/2.7/multiprocessing/connection.py b/lib-python/2.7/multiprocessing/connection.py
--- a/lib-python/2.7/multiprocessing/connection.py
+++ b/lib-python/2.7/multiprocessing/connection.py
@@ -186,6 +186,8 @@
         '''
         if duplex:
             s1, s2 = socket.socketpair()
+            s1.setblocking(True)
+            s2.setblocking(True)
             c1 = _multiprocessing.Connection(os.dup(s1.fileno()))
             c2 = _multiprocessing.Connection(os.dup(s2.fileno()))
             s1.close()
@@ -198,7 +200,6 @@
         return c1, c2
 
 else:
-
     from _multiprocessing import win32
 
     def Pipe(duplex=True):
@@ -251,6 +252,7 @@
         self._socket = socket.socket(getattr(socket, family))
         try:
             self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            self._socket.setblocking(True)
             self._socket.bind(address)
             self._socket.listen(backlog)
             self._address = self._socket.getsockname()
@@ -269,6 +271,7 @@
 
     def accept(self):
         s, self._last_accepted = self._socket.accept()
+        s.setblocking(True)
         fd = duplicate(s.fileno())
         conn = _multiprocessing.Connection(fd)
         s.close()
@@ -286,6 +289,7 @@
     '''
     family = address_type(address)
     s = socket.socket( getattr(socket, family) )
+    s.setblocking(True)
     t = _init_timeout()
 
     while 1:
@@ -348,7 +352,10 @@
             try:
                 win32.ConnectNamedPipe(handle, win32.NULL)
             except WindowsError, e:
-                if e.args[0] != win32.ERROR_PIPE_CONNECTED:
+                # ERROR_NO_DATA can occur if a client has already connected,
+                # written data and then disconnected -- see Issue 14725.
+                if e.args[0] not in (win32.ERROR_PIPE_CONNECTED,
+                                     win32.ERROR_NO_DATA):
                     raise
             return _multiprocessing.PipeConnection(handle)
 
diff --git a/lib-python/2.7/multiprocessing/dummy/__init__.py b/lib-python/2.7/multiprocessing/dummy/__init__.py
--- a/lib-python/2.7/multiprocessing/dummy/__init__.py
+++ b/lib-python/2.7/multiprocessing/dummy/__init__.py
@@ -70,7 +70,8 @@
     def start(self):
         assert self._parent is current_process()
         self._start_called = True
-        self._parent._children[self] = None
+        if hasattr(self._parent, '_children'):
+            self._parent._children[self] = None
         threading.Thread.start(self)
 
     @property
diff --git a/lib-python/2.7/multiprocessing/forking.py b/lib-python/2.7/multiprocessing/forking.py
--- a/lib-python/2.7/multiprocessing/forking.py
+++ b/lib-python/2.7/multiprocessing/forking.py
@@ -35,6 +35,7 @@
 import os
 import sys
 import signal
+import errno
 
 from multiprocessing import util, process
 
@@ -129,12 +130,17 @@
 
         def poll(self, flag=os.WNOHANG):
             if self.returncode is None:
-                try:
-                    pid, sts = os.waitpid(self.pid, flag)
-                except os.error:
-                    # Child process not yet created. See #1731717
-                    # e.errno == errno.ECHILD == 10
-                    return None
+                while True:
+                    try:
+                        pid, sts = os.waitpid(self.pid, flag)
+                    except os.error as e:
+                        if e.errno == errno.EINTR:
+                            continue
+                        # Child process not yet created. See #1731717
+                        # e.errno == errno.ECHILD == 10
+                        return None
+                    else:
+                        break
                 if pid == self.pid:
                     if os.WIFSIGNALED(sts):
                         self.returncode = -os.WTERMSIG(sts)
@@ -336,7 +342,7 @@
         '''
         Returns prefix of command line used for spawning a child process
         '''
-        if process.current_process()._identity==() and is_forking(sys.argv):
+        if getattr(process.current_process(), '_inheriting', False):
             raise RuntimeError('''
             Attempt to start a new process before the current process
             has finished its bootstrapping phase.
diff --git a/lib-python/2.7/multiprocessing/pool.py b/lib-python/2.7/multiprocessing/pool.py
--- a/lib-python/2.7/multiprocessing/pool.py
+++ b/lib-python/2.7/multiprocessing/pool.py
@@ -68,6 +68,23 @@
 # Code run by worker processes
 #
 
+class MaybeEncodingError(Exception):
+    """Wraps possible unpickleable errors, so they can be
+    safely sent through the socket."""
+
+    def __init__(self, exc, value):
+        self.exc = repr(exc)
+        self.value = repr(value)
+        super(MaybeEncodingError, self).__init__(self.exc, self.value)
+
+    def __str__(self):
+        return "Error sending result: '%s'. Reason: '%s'" % (self.value,
+                                                             self.exc)
+
+    def __repr__(self):
+        return "<MaybeEncodingError: %s>" % str(self)
+
+
 def worker(inqueue, outqueue, initializer=None, initargs=(), maxtasks=None):
     assert maxtasks is None or (type(maxtasks) == int and maxtasks > 0)
     put = outqueue.put
@@ -96,7 +113,13 @@
             result = (True, func(*args, **kwds))
         except Exception, e:
             result = (False, e)
-        put((job, i, result))
+        try:
+            put((job, i, result))
+        except Exception as e:
+            wrapped = MaybeEncodingError(e, result[1])
+            debug("Possible encoding error while sending result: %s" % (
+                wrapped))
+            put((job, i, (False, wrapped)))
         completed += 1
     debug('worker exiting after %d tasks' % completed)
 
@@ -466,7 +489,8 @@
         # We must wait for the worker handler to exit before terminating
         # workers because we don't want workers to be restarted behind our back.
         debug('joining worker handler')
-        worker_handler.join()
+        if threading.current_thread() is not worker_handler:
+            worker_handler.join(1e100)
 
         # Terminate workers which haven't already finished.
         if pool and hasattr(pool[0], 'terminate'):
@@ -476,10 +500,12 @@
                     p.terminate()
 
         debug('joining task handler')
-        task_handler.join(1e100)
+        if threading.current_thread() is not task_handler:
+            task_handler.join(1e100)
 
         debug('joining result handler')
-        result_handler.join(1e100)
+        if threading.current_thread() is not result_handler:
+            result_handler.join(1e100)
 
         if pool and hasattr(pool[0], 'terminate'):
             debug('joining pool workers')
@@ -553,6 +579,7 @@
         if chunksize <= 0:
             self._number_left = 0
             self._ready = True
+            del cache[self._job]
         else:
             self._number_left = length//chunksize + bool(length % chunksize)
 
diff --git a/lib-python/2.7/multiprocessing/process.py b/lib-python/2.7/multiprocessing/process.py
--- a/lib-python/2.7/multiprocessing/process.py
+++ b/lib-python/2.7/multiprocessing/process.py
@@ -262,12 +262,12 @@
         except SystemExit, e:
             if not e.args:
                 exitcode = 1
-            elif type(e.args[0]) is int:
+            elif isinstance(e.args[0], int):
                 exitcode = e.args[0]
             else:
-                sys.stderr.write(e.args[0] + '\n')
+                sys.stderr.write(str(e.args[0]) + '\n')
                 sys.stderr.flush()
-                exitcode = 1
+                exitcode = 0 if isinstance(e.args[0], str) else 1
         except:
             exitcode = 1
             import traceback
diff --git a/lib-python/2.7/multiprocessing/util.py b/lib-python/2.7/multiprocessing/util.py
--- a/lib-python/2.7/multiprocessing/util.py
+++ b/lib-python/2.7/multiprocessing/util.py
@@ -247,6 +247,12 @@
     Finalizers with highest priority are called first; finalizers with
     the same priority will be called in reverse order of creation.
     '''
+    if _finalizer_registry is None:
+        # This function may be called after this module's globals are
+        # destroyed.  See the _exit_function function in this module for more
+        # notes.
+        return
+
     if minpriority is None:
         f = lambda p : p[0][0] is not None
     else:
@@ -278,21 +284,38 @@
 
 _exiting = False
 
-def _exit_function():
+def _exit_function(info=info, debug=debug, _run_finalizers=_run_finalizers,
+                   active_children=active_children,
+                   current_process=current_process):
+    # NB: we hold on to references to functions in the arglist due to the
+    # situation described below, where this function is called after this
+    # module's globals are destroyed.
+
     global _exiting
 
     info('process shutting down')
     debug('running all "atexit" finalizers with priority >= 0')
     _run_finalizers(0)
 
-    for p in active_children():
-        if p._daemonic:
-            info('calling terminate() for daemon %s', p.name)
-            p._popen.terminate()
+    if current_process() is not None:
+        # NB: we check if the current process is None here because if
+        # it's None, any call to ``active_children()`` will throw an
+        # AttributeError (active_children winds up trying to get
+        # attributes from util._current_process).  This happens in a
+        # variety of shutdown circumstances that are not well-understood
+        # because module-scope variables are not apparently supposed to
+        # be destroyed until after this function is called.  However,
+        # they are indeed destroyed before this function is called.  See
+        # issues 9775 and 15881.  Also related: 4106, 9205, and 9207.
 
-    for p in active_children():
-        info('calling join() for process %s', p.name)
-        p.join()
+        for p in active_children():
+            if p._daemonic:
+                info('calling terminate() for daemon %s', p.name)
+                p._popen.terminate()
+
+        for p in active_children():
+            info('calling join() for process %s', p.name)
+            p.join()
 
     debug('running the remaining "atexit" finalizers')
     _run_finalizers()
diff --git a/lib-python/2.7/plat-generic/regen b/lib-python/2.7/plat-generic/regen
--- a/lib-python/2.7/plat-generic/regen
+++ b/lib-python/2.7/plat-generic/regen
@@ -1,3 +1,3 @@
 #! /bin/sh
 set -v
-python$EXE ../../Tools/scripts/h2py.py -i '(u_long)' /usr/include/netinet/in.h
+eval $PYTHON_FOR_BUILD ../../Tools/scripts/h2py.py -i "'(u_long)'" /usr/include/netinet/in.h
diff --git a/lib-python/2.7/platform.py b/lib-python/2.7/platform.py
--- a/lib-python/2.7/platform.py
+++ b/lib-python/2.7/platform.py
@@ -673,8 +673,13 @@
                     release = '7'
                 else:
                     release = '2008ServerR2'
+            elif min == 2:
+                if product_type == VER_NT_WORKSTATION:
+                    release = '8'
+                else:
+                    release = '2012Server'
             else:
-                release = 'post2008Server'
+                release = 'post2012Server'
 
     else:
         if not release:
@@ -1020,16 +1025,38 @@
         case the command should fail.
 
     """
+
+    # We do the import here to avoid a bootstrap issue.
+    # See c73b90b6dadd changeset.
+    #
+    # [..]
+    # ranlib libpython2.7.a
+    # gcc   -o python \
+    #        Modules/python.o \
+    #        libpython2.7.a -lsocket -lnsl -ldl    -lm
+    # Traceback (most recent call last):
+    #  File "./setup.py", line 8, in <module>
+    #    from platform import machine as platform_machine
+    #  File "[..]/build/Lib/platform.py", line 116, in <module>
+    #    import sys,string,os,re,subprocess
+    #  File "[..]/build/Lib/subprocess.py", line 429, in <module>
+    #    import select
+    # ImportError: No module named select
+
+    import subprocess
+
     if sys.platform in ('dos','win32','win16','os2'):
         # XXX Others too ?
         return default
-    target = _follow_symlinks(target).replace('"', '\\"')
+    target = _follow_symlinks(target)
     try:
-        f = os.popen('file "%s" 2> %s' % (target, DEV_NULL))
+        proc = subprocess.Popen(['file', target],
+                stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
     except (AttributeError,os.error):
         return default
-    output = string.strip(f.read())
-    rc = f.close()
+    output = proc.communicate()[0]
+    rc = proc.wait()
     if not output or rc:
         return default
     else:
diff --git a/lib-python/2.7/posixpath.py b/lib-python/2.7/posixpath.py
--- a/lib-python/2.7/posixpath.py
+++ b/lib-python/2.7/posixpath.py
@@ -17,6 +17,14 @@
 import warnings
 from genericpath import *
 
+try:
+    _unicode = unicode
+except NameError:
+    # If Python is built without Unicode support, the unicode type
+    # will not exist. Fake one.
+    class _unicode(object):
+        pass
+
 __all__ = ["normcase","isabs","join","splitdrive","split","splitext",
            "basename","dirname","commonprefix","getsize","getmtime",
            "getatime","getctime","islink","exists","lexists","isdir","isfile",
@@ -60,7 +68,8 @@
 def join(a, *p):
     """Join two or more pathname components, inserting '/' as needed.
     If any component is an absolute path, all previous path components
-    will be discarded."""
+    will be discarded.  An empty last part will result in a path that
+    ends with a separator."""
     path = a
     for b in p:
         if b.startswith('/'):
@@ -267,8 +276,8 @@
         except KeyError:
             return path
         userhome = pwent.pw_dir
-    userhome = userhome.rstrip('/') or userhome
-    return userhome + path[i:]
+    userhome = userhome.rstrip('/')
+    return (userhome + path[i:]) or '/'
 
 
 # Expand paths containing shell variable substitutions.
@@ -312,7 +321,7 @@
 def normpath(path):
     """Normalize path, eliminating double slashes, etc."""
     # Preserve unicode (if path is unicode)
-    slash, dot = (u'/', u'.') if isinstance(path, unicode) else ('/', '.')
+    slash, dot = (u'/', u'.') if isinstance(path, _unicode) else ('/', '.')
     if path == '':
         return dot
     initial_slashes = path.startswith('/')
@@ -341,7 +350,7 @@
 def abspath(path):
     """Return an absolute path."""
     if not isabs(path):
-        if isinstance(path, unicode):
+        if isinstance(path, _unicode):
             cwd = os.getcwdu()
         else:
             cwd = os.getcwd()
@@ -355,46 +364,53 @@
 def realpath(filename):
     """Return the canonical path of the specified filename, eliminating any
 symbolic links encountered in the path."""
-    if isabs(filename):
-        bits = ['/'] + filename.split('/')[1:]
-    else:
-        bits = [''] + filename.split('/')
+    path, ok = _joinrealpath('', filename, {})
+    return abspath(path)
 
-    for i in range(2, len(bits)+1):
-        component = join(*bits[0:i])
-        # Resolve symbolic links.
-        if islink(component):
-            resolved = _resolve_link(component)
-            if resolved is None:
-                # Infinite loop -- return original component + rest of the path
-                return abspath(join(*([component] + bits[i:])))
+# Join two paths, normalizing ang eliminating any symbolic links
+# encountered in the second path.
+def _joinrealpath(path, rest, seen):
+    if isabs(rest):
+        rest = rest[1:]
+        path = sep
+
+    while rest:
+        name, _, rest = rest.partition(sep)
+        if not name or name == curdir:
+            # current dir
+            continue
+        if name == pardir:
+            # parent dir
+            if path:
+                path, name = split(path)
+                if name == pardir:
+                    path = join(path, pardir, pardir)
             else:
-                newpath = join(*([resolved] + bits[i:]))
-                return realpath(newpath)
+                path = pardir
+            continue
+        newpath = join(path, name)
+        if not islink(newpath):
+            path = newpath
+            continue
+        # Resolve the symbolic link
+        if newpath in seen:
+            # Already seen this path
+            path = seen[newpath]
+            if path is not None:
+                # use cached value
+                continue
+            # The symlink is not resolved, so we must have a symlink loop.
+            # Return already resolved part + rest of the path unchanged.
+            return join(newpath, rest), False
+        seen[newpath] = None # not resolved symlink
+        path, ok = _joinrealpath(path, os.readlink(newpath), seen)
+        if not ok:
+            return join(path, rest), False
+        seen[newpath] = path # resolved symlink
 
-    return abspath(filename)
+    return path, True
 
 
-def _resolve_link(path):
-    """Internal helper function.  Takes a path and follows symlinks
-    until we either arrive at something that isn't a symlink, or
-    encounter a path we've seen before (meaning that there's a loop).
-    """
-    paths_seen = set()
-    while islink(path):
-        if path in paths_seen:
-            # Already seen this path, so we must have a symlink loop
-            return None
-        paths_seen.add(path)
-        # Resolve where the link points to
-        resolved = os.readlink(path)
-        if not isabs(resolved):
-            dir = dirname(path)
-            path = normpath(join(dir, resolved))
-        else:
-            path = normpath(resolved)
-    return path
-
 supports_unicode_filenames = (sys.platform == 'darwin')
 
 def relpath(path, start=curdir):
diff --git a/lib-python/2.7/pstats.py b/lib-python/2.7/pstats.py
--- a/lib-python/2.7/pstats.py
+++ b/lib-python/2.7/pstats.py
@@ -120,8 +120,8 @@
             self.stats = arg.stats
             arg.stats = {}
         if not self.stats:
-            raise TypeError,  "Cannot create or construct a %r object from '%r''" % (
-                              self.__class__, arg)
+            raise TypeError("Cannot create or construct a %r object from %r"
+                            % (self.__class__, arg))
         return
 
     def get_top_level_stats(self):
@@ -172,15 +172,19 @@
     # along with some printable description
     sort_arg_dict_default = {
               "calls"     : (((1,-1),              ), "call count"),
+              "ncalls"    : (((1,-1),              ), "call count"),
+              "cumtime"   : (((3,-1),              ), "cumulative time"),
               "cumulative": (((3,-1),              ), "cumulative time"),
               "file"      : (((4, 1),              ), "file name"),
+              "filename"  : (((4, 1),              ), "file name"),
               "line"      : (((5, 1),              ), "line number"),
               "module"    : (((4, 1),              ), "file name"),
               "name"      : (((6, 1),              ), "function name"),
               "nfl"       : (((6, 1),(4, 1),(5, 1),), "name/file/line"),
-              "pcalls"    : (((0,-1),              ), "call count"),
+              "pcalls"    : (((0,-1),              ), "primitive call count"),
               "stdname"   : (((7, 1),              ), "standard name"),
               "time"      : (((2,-1),              ), "internal time"),
+              "tottime"   : (((2,-1),              ), "internal time"),
               }
 
     def get_sort_arg_defs(self):
diff --git a/lib-python/2.7/py_compile.py b/lib-python/2.7/py_compile.py
--- a/lib-python/2.7/py_compile.py
+++ b/lib-python/2.7/py_compile.py
@@ -112,7 +112,7 @@
     try:
         codeobject = __builtin__.compile(codestring, dfile or file,'exec')
     except Exception,err:
-        py_exc = PyCompileError(err.__class__,err.args,dfile or file)
+        py_exc = PyCompileError(err.__class__, err, dfile or file)
         if doraise:
             raise py_exc
         else:
diff --git a/lib-python/2.7/pyclbr.py b/lib-python/2.7/pyclbr.py
--- a/lib-python/2.7/pyclbr.py
+++ b/lib-python/2.7/pyclbr.py
@@ -128,6 +128,8 @@
         parent = _readmodule(package, path, inpackage)
         if inpackage is not None:
             package = "%s.%s" % (inpackage, package)
+        if not '__path__' in parent:
+            raise ImportError('No package named {}'.format(package))
         return _readmodule(submodule, parent['__path__'], package)
 
     # Search the path for the module
diff --git a/lib-python/2.7/pydoc.py b/lib-python/2.7/pydoc.py
--- a/lib-python/2.7/pydoc.py
+++ b/lib-python/2.7/pydoc.py
@@ -1498,7 +1498,8 @@
             raise ImportError, 'no Python documentation found for %r' % thing
         return object, thing
     else:
-        return thing, getattr(thing, '__name__', None)
+        name = getattr(thing, '__name__', None)
+        return thing, name if isinstance(name, str) else None
 
 def render_doc(thing, title='Python Library Documentation: %s', forceload=0):
     """Render text documentation, given an object or a path to an object."""
@@ -1799,7 +1800,7 @@
 Welcome to Python %s!  This is the online help utility.
 
 If this is your first time using Python, you should definitely check out
-the tutorial on the Internet at http://docs.python.org/tutorial/.
+the tutorial on the Internet at http://docs.python.org/%s/tutorial/.
 
 Enter the name of any module, keyword, or topic to get help on writing
 Python programs and using Python modules.  To quit this help utility and
@@ -1809,7 +1810,7 @@
 "keywords", or "topics".  Each module also comes with a one-line summary
 of what it does; to list the modules whose summaries contain a given word
 such as "spam", type "modules spam".
-''' % sys.version[:3])
+''' % tuple([sys.version[:3]]*2))
 
     def list(self, items, columns=4, width=80):
         items = items[:]
diff --git a/lib-python/2.7/random.py b/lib-python/2.7/random.py
--- a/lib-python/2.7/random.py
+++ b/lib-python/2.7/random.py
@@ -457,27 +457,25 @@
         if kappa <= 1e-6:
             return TWOPI * random()
 
-        a = 1.0 + _sqrt(1.0 + 4.0 * kappa * kappa)
-        b = (a - _sqrt(2.0 * a))/(2.0 * kappa)
-        r = (1.0 + b * b)/(2.0 * b)
+        s = 0.5 / kappa
+        r = s + _sqrt(1.0 + s * s)
 
         while 1:
             u1 = random()
+            z = _cos(_pi * u1)
 
-            z = _cos(_pi * u1)
-            f = (1.0 + r * z)/(r + z)
-            c = kappa * (r - f)
-
+            d = z / (r + z)
             u2 = random()
-
-            if u2 < c * (2.0 - c) or u2 <= c * _exp(1.0 - c):
+            if u2 < 1.0 - d * d or u2 <= (1.0 - d) * _exp(d):
                 break
 
+        q = 1.0 / r
+        f = (q + z) / (1.0 + q * z)
         u3 = random()
         if u3 > 0.5:
-            theta = (mu % TWOPI) + _acos(f)
+            theta = (mu + _acos(f)) % TWOPI
         else:
-            theta = (mu % TWOPI) - _acos(f)
+            theta = (mu - _acos(f)) % TWOPI
 
         return theta
 
diff --git a/lib-python/2.7/rfc822.py b/lib-python/2.7/rfc822.py
--- a/lib-python/2.7/rfc822.py
+++ b/lib-python/2.7/rfc822.py
@@ -212,7 +212,7 @@
         You may override this method if your application wants to bend the
         rules, e.g. to strip trailing whitespace, or to recognize MH template
         separators ('--------').  For convenience (e.g. for code reading from
-        sockets) a line consisting of \r\n also matches.
+        sockets) a line consisting of \\r\\n also matches.
         """
         return line in _blanklines
 
diff --git a/lib-python/2.7/rlcompleter.py b/lib-python/2.7/rlcompleter.py
--- a/lib-python/2.7/rlcompleter.py
+++ b/lib-python/2.7/rlcompleter.py
@@ -1,13 +1,11 @@
-"""Word completion for GNU readline 2.0.
+"""Word completion for GNU readline.
 
-This requires the latest extension to the readline module. The completer
-completes keywords, built-ins and globals in a selectable namespace (which
-defaults to __main__); when completing NAME.NAME..., it evaluates (!) the
-expression up to the last dot and completes its attributes.
+The completer completes keywords, built-ins and globals in a selectable
+namespace (which defaults to __main__); when completing NAME.NAME..., it
+evaluates (!) the expression up to the last dot and completes its attributes.
 
-It's very cool to do "import sys" type "sys.", hit the
-completion key (twice), and see the list of names defined by the
-sys module!
+It's very cool to do "import sys" type "sys.", hit the completion key (twice),
+and see the list of names defined by the sys module!
 
 Tip: to use the tab key as the completion key, call
 
@@ -15,18 +13,16 @@
 
 Notes:
 
-- Exceptions raised by the completer function are *ignored* (and
-generally cause the completion to fail).  This is a feature -- since
-readline sets the tty device in raw (or cbreak) mode, printing a
-traceback wouldn't work well without some complicated hoopla to save,
-reset and restore the tty state.
+- Exceptions raised by the completer function are *ignored* (and generally cause
+  the completion to fail).  This is a feature -- since readline sets the tty
+  device in raw (or cbreak) mode, printing a traceback wouldn't work well
+  without some complicated hoopla to save, reset and restore the tty state.
 
-- The evaluation of the NAME.NAME... form may cause arbitrary
-application defined code to be executed if an object with a
-__getattr__ hook is found.  Since it is the responsibility of the
-application (or the user) to enable this feature, I consider this an
-acceptable risk.  More complicated expressions (e.g. function calls or
-indexing operations) are *not* evaluated.
+- The evaluation of the NAME.NAME... form may cause arbitrary application
+  defined code to be executed if an object with a __getattr__ hook is found.
+  Since it is the responsibility of the application (or the user) to enable this
+  feature, I consider this an acceptable risk.  More complicated expressions
+  (e.g. function calls or indexing operations) are *not* evaluated.
 
 - GNU readline is also used by the built-in functions input() and
 raw_input(), and thus these also benefit/suffer from the completer
@@ -35,7 +31,7 @@
 its input.
 
 - When the original stdin is not a tty device, GNU readline is never
-used, and this module (and the readline module) are silently inactive.
+  used, and this module (and the readline module) are silently inactive.
 
 """
 
diff --git a/lib-python/2.7/runpy.py b/lib-python/2.7/runpy.py
--- a/lib-python/2.7/runpy.py
+++ b/lib-python/2.7/runpy.py
@@ -200,7 +200,7 @@
                 pass
         else:
             # The following check looks a bit odd. The trick is that
-            # NullImporter throws ImportError if the supplied path is a
+            # NullImporter raises ImportError if the supplied path is a
             # *valid* directory entry (and hence able to be handled
             # by the standard import machinery)
             try:
diff --git a/lib-python/2.7/shutil.py b/lib-python/2.7/shutil.py
--- a/lib-python/2.7/shutil.py
+++ b/lib-python/2.7/shutil.py
@@ -102,8 +102,10 @@
         try:
             os.chflags(dst, st.st_flags)
         except OSError, why:
-            if (not hasattr(errno, 'EOPNOTSUPP') or
-                why.errno != errno.EOPNOTSUPP):
+            for err in 'EOPNOTSUPP', 'ENOTSUP':
+                if hasattr(errno, err) and why.errno == getattr(errno, err):
+                    break
+            else:
                 raise
 
 def copy(src, dst):
@@ -201,7 +203,7 @@
             # Copying file access times may fail on Windows
             pass
         else:
-            errors.extend((src, dst, str(why)))
+            errors.append((src, dst, str(why)))
     if errors:
         raise Error, errors
 
diff --git a/lib-python/2.7/smtplib.py b/lib-python/2.7/smtplib.py
--- a/lib-python/2.7/smtplib.py
+++ b/lib-python/2.7/smtplib.py
@@ -818,13 +818,13 @@
         try:
             self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
             self.sock.connect(host)
-        except socket.error, msg:
+        except socket.error:
             if self.debuglevel > 0:
                 print>>stderr, 'connect fail:', host
             if self.sock:
                 self.sock.close()
             self.sock = None
-            raise socket.error, msg
+            raise
         (code, msg) = self.getreply()
         if self.debuglevel > 0:
             print>>stderr, "connect:", msg
diff --git a/lib-python/2.7/socket.py b/lib-python/2.7/socket.py
--- a/lib-python/2.7/socket.py
+++ b/lib-python/2.7/socket.py
@@ -319,8 +319,8 @@
         self._wbuf.append(data)
         self._wbuf_len += len(data)
         if (self._wbufsize == 0 or
-            self._wbufsize == 1 and '\n' in data or
-            self._wbuf_len >= self._wbufsize):
+            (self._wbufsize == 1 and '\n' in data) or
+            (self._wbufsize > 1 and self._wbuf_len >= self._wbufsize)):
             self.flush()
 
     def writelines(self, list):
diff --git a/lib-python/2.7/sqlite3/dbapi2.py b/lib-python/2.7/sqlite3/dbapi2.py
--- a/lib-python/2.7/sqlite3/dbapi2.py
+++ b/lib-python/2.7/sqlite3/dbapi2.py
@@ -1,4 +1,4 @@
-#-*- coding: ISO-8859-1 -*-
+# -*- coding: iso-8859-1 -*-
 # pysqlite2/dbapi2.py: the DB-API 2.0 interface
 #
 # Copyright (C) 2004-2005 Gerhard Häring <gh at ghaering.de>
@@ -68,7 +68,7 @@
         timepart_full = timepart.split(".")
         hours, minutes, seconds = map(int, timepart_full[0].split(":"))
         if len(timepart_full) == 2:
-            microseconds = int(timepart_full[1])
+            microseconds = int('{:0<6.6}'.format(timepart_full[1].decode()))
         else:
             microseconds = 0
 
diff --git a/lib-python/2.7/sqlite3/dump.py b/lib-python/2.7/sqlite3/dump.py
--- a/lib-python/2.7/sqlite3/dump.py
+++ b/lib-python/2.7/sqlite3/dump.py
@@ -25,9 +25,10 @@
         FROM "sqlite_master"
             WHERE "sql" NOT NULL AND
             "type" == 'table'
+            ORDER BY "name"
         """
     schema_res = cu.execute(q)
-    for table_name, type, sql in sorted(schema_res.fetchall()):
+    for table_name, type, sql in schema_res.fetchall():
         if table_name == 'sqlite_sequence':
             yield('DELETE FROM "sqlite_sequence";')
         elif table_name == 'sqlite_stat1':
@@ -42,7 +43,7 @@
         #        qtable,
         #        sql.replace("''")))
         else:
-            yield('{0};'.format(sql))
+            yield('%s;' % sql)
 
         # Build the insert statement for each row of the current table
         table_name_ident = table_name.replace('"', '""')
@@ -53,7 +54,7 @@
             ",".join("""'||quote("{0}")||'""".format(col.replace('"', '""')) for col in column_names))
         query_res = cu.execute(q)
         for row in query_res:
-            yield("{0};".format(row[0]))
+            yield("%s;" % row[0])
 
     # Now when the type is 'index', 'trigger', or 'view'
     q = """
@@ -64,6 +65,6 @@
         """
     schema_res = cu.execute(q)
     for name, type, sql in schema_res.fetchall():
-        yield('{0};'.format(sql))
+        yield('%s;' % sql)
 
     yield('COMMIT;')
diff --git a/lib-python/2.7/sqlite3/test/dump.py b/lib-python/2.7/sqlite3/test/dump.py
--- a/lib-python/2.7/sqlite3/test/dump.py
+++ b/lib-python/2.7/sqlite3/test/dump.py
@@ -29,6 +29,8 @@
                 ,
                 "INSERT INTO \"t1\" VALUES(2,'foo2',30,30);"
                 ,
+                u"INSERT INTO \"t1\" VALUES(3,'f\xc3\xb6',40,10);"
+                ,
                 "CREATE TABLE t2(id integer, t2_i1 integer, " \
                 "t2_i2 integer, primary key (id)," \
                 "foreign key(t2_i1) references t1(t1_i1));"
@@ -49,6 +51,27 @@
         [self.assertEqual(expected_sqls[i], actual_sqls[i])
             for i in xrange(len(expected_sqls))]
 
+    def CheckUnorderableRow(self):
+        # iterdump() should be able to cope with unorderable row types (issue #15545)
+        class UnorderableRow:
+            def __init__(self, cursor, row):
+                self.row = row
+            def __getitem__(self, index):
+                return self.row[index]
+        self.cx.row_factory = UnorderableRow
+        CREATE_ALPHA = """CREATE TABLE "alpha" ("one");"""
+        CREATE_BETA = """CREATE TABLE "beta" ("two");"""
+        expected = [
+            "BEGIN TRANSACTION;",
+            CREATE_ALPHA,
+            CREATE_BETA,
+            "COMMIT;"
+            ]
+        self.cu.execute(CREATE_BETA)
+        self.cu.execute(CREATE_ALPHA)
+        got = list(self.cx.iterdump())
+        self.assertEqual(expected, got)
+
 def suite():
     return unittest.TestSuite(unittest.makeSuite(DumpTests, "Check"))
 
diff --git a/lib-python/2.7/sqlite3/test/hooks.py b/lib-python/2.7/sqlite3/test/hooks.py
--- a/lib-python/2.7/sqlite3/test/hooks.py
+++ b/lib-python/2.7/sqlite3/test/hooks.py
@@ -76,6 +76,25 @@
         except sqlite.OperationalError, e:
             self.assertEqual(e.args[0].lower(), "no such collation sequence: mycoll")
 
+    def CheckCollationReturnsLargeInteger(self):
+        def mycoll(x, y):
+            # reverse order
+            return -((x > y) - (x < y)) * 2**32
+        con = sqlite.connect(":memory:")
+        con.create_collation("mycoll", mycoll)
+        sql = """
+            select x from (
+            select 'a' as x
+            union
+            select 'b' as x
+            union
+            select 'c' as x
+            ) order by x collate mycoll
+            """
+        result = con.execute(sql).fetchall()
+        self.assertEqual(result, [('c',), ('b',), ('a',)],
+                         msg="the expected order was not returned")
+
     def CheckCollationRegisterTwice(self):
         """
         Register two different collation functions under the same name.
diff --git a/lib-python/2.7/sqlite3/test/regression.py b/lib-python/2.7/sqlite3/test/regression.py
--- a/lib-python/2.7/sqlite3/test/regression.py
+++ b/lib-python/2.7/sqlite3/test/regression.py
@@ -1,4 +1,4 @@
-#-*- coding: ISO-8859-1 -*-
+#-*- coding: iso-8859-1 -*-
 # pysqlite2/test/regression.py: pysqlite regression tests
 #
 # Copyright (C) 2006-2007 Gerhard Häring <gh at ghaering.de>
@@ -285,6 +285,32 @@
             cur.executemany("insert into b (baz) values (?)",
                             ((i,) for i in foo()))
 
+    def CheckConvertTimestampMicrosecondPadding(self):
+        """
+        http://bugs.python.org/issue14720
+
+        The microsecond parsing of convert_timestamp() should pad with zeros,
+        since the microsecond string "456" actually represents "456000".
+        """
+
+        con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
+        cur = con.cursor()
+        cur.execute("CREATE TABLE t (x TIMESTAMP)")
+
+        # Microseconds should be 456000
+        cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.456')")
+
+        # Microseconds should be truncated to 123456
+        cur.execute("INSERT INTO t (x) VALUES ('2012-04-04 15:06:00.123456789')")
+
+        cur.execute("SELECT * FROM t")
+        values = [x[0] for x in cur.fetchall()]
+
+        self.assertEqual(values, [
+            datetime.datetime(2012, 4, 4, 15, 6, 0, 456000),
+            datetime.datetime(2012, 4, 4, 15, 6, 0, 123456),
+        ])
+
 
 def suite():
     regression_suite = unittest.makeSuite(RegressionTests, "Check")
diff --git a/lib-python/2.7/sqlite3/test/userfunctions.py b/lib-python/2.7/sqlite3/test/userfunctions.py
--- a/lib-python/2.7/sqlite3/test/userfunctions.py
+++ b/lib-python/2.7/sqlite3/test/userfunctions.py
@@ -374,14 +374,15 @@
         val = cur.fetchone()[0]
         self.assertEqual(val, 60)
 
-def authorizer_cb(action, arg1, arg2, dbname, source):
-    if action != sqlite.SQLITE_SELECT:
-        return sqlite.SQLITE_DENY
-    if arg2 == 'c2' or arg1 == 't2':
-        return sqlite.SQLITE_DENY
-    return sqlite.SQLITE_OK
+class AuthorizerTests(unittest.TestCase):
+    @staticmethod
+    def authorizer_cb(action, arg1, arg2, dbname, source):
+        if action != sqlite.SQLITE_SELECT:
+            return sqlite.SQLITE_DENY
+        if arg2 == 'c2' or arg1 == 't2':
+            return sqlite.SQLITE_DENY
+        return sqlite.SQLITE_OK
 
-class AuthorizerTests(unittest.TestCase):
     def setUp(self):
         self.con = sqlite.connect(":memory:")
         self.con.executescript("""
@@ -394,12 +395,12 @@
         # For our security test:
         self.con.execute("select c2 from t2")
 
-        self.con.set_authorizer(authorizer_cb)
+        self.con.set_authorizer(self.authorizer_cb)
 
     def tearDown(self):
         pass
 
-    def CheckTableAccess(self):
+    def test_table_access(self):
         try:
             self.con.execute("select * from t2")
         except sqlite.DatabaseError, e:
@@ -408,7 +409,7 @@
             return
         self.fail("should have raised an exception due to missing privileges")
 
-    def CheckColumnAccess(self):
+    def test_column_access(self):
         try:
             self.con.execute("select c2 from t1")
         except sqlite.DatabaseError, e:
@@ -417,11 +418,46 @@
             return
         self.fail("should have raised an exception due to missing privileges")
 
+class AuthorizerRaiseExceptionTests(AuthorizerTests):
+    @staticmethod
+    def authorizer_cb(action, arg1, arg2, dbname, source):
+        if action != sqlite.SQLITE_SELECT:
+            raise ValueError
+        if arg2 == 'c2' or arg1 == 't2':
+            raise ValueError
+        return sqlite.SQLITE_OK
+
+class AuthorizerIllegalTypeTests(AuthorizerTests):
+    @staticmethod
+    def authorizer_cb(action, arg1, arg2, dbname, source):
+        if action != sqlite.SQLITE_SELECT:
+            return 0.0
+        if arg2 == 'c2' or arg1 == 't2':
+            return 0.0
+        return sqlite.SQLITE_OK
+
+class AuthorizerLargeIntegerTests(AuthorizerTests):
+    @staticmethod
+    def authorizer_cb(action, arg1, arg2, dbname, source):
+        if action != sqlite.SQLITE_SELECT:
+            return 2**32
+        if arg2 == 'c2' or arg1 == 't2':
+            return 2**32
+        return sqlite.SQLITE_OK
+
+
 def suite():
     function_suite = unittest.makeSuite(FunctionTests, "Check")
     aggregate_suite = unittest.makeSuite(AggregateTests, "Check")
-    authorizer_suite = unittest.makeSuite(AuthorizerTests, "Check")
-    return unittest.TestSuite((function_suite, aggregate_suite, authorizer_suite))
+    authorizer_suite = unittest.makeSuite(AuthorizerTests)
+    return unittest.TestSuite((
+            function_suite,
+            aggregate_suite,
+            authorizer_suite,
+            unittest.makeSuite(AuthorizerRaiseExceptionTests),
+            unittest.makeSuite(AuthorizerIllegalTypeTests),
+            unittest.makeSuite(AuthorizerLargeIntegerTests),
+        ))
 
 def test():
     runner = unittest.TextTestRunner()
diff --git a/lib-python/2.7/sre_compile.py b/lib-python/2.7/sre_compile.py
--- a/lib-python/2.7/sre_compile.py
+++ b/lib-python/2.7/sre_compile.py
@@ -13,6 +13,7 @@
 import _sre, sys
 import sre_parse
 from sre_constants import *
+from _sre import MAXREPEAT
 
 assert _sre.MAGIC == MAGIC, "SRE module mismatch"
 
diff --git a/lib-python/2.7/sre_constants.py b/lib-python/2.7/sre_constants.py
--- a/lib-python/2.7/sre_constants.py
+++ b/lib-python/2.7/sre_constants.py
@@ -15,9 +15,7 @@
 
 MAGIC = 20031017
 
-# max code word in this release
-
-MAXREPEAT = 65535
+from _sre import MAXREPEAT
 
 # SRE standard exception (access as sre.error)
 # should this really be here?
diff --git a/lib-python/2.7/sre_parse.py b/lib-python/2.7/sre_parse.py
--- a/lib-python/2.7/sre_parse.py
+++ b/lib-python/2.7/sre_parse.py
@@ -15,6 +15,7 @@
 import sys
 
 from sre_constants import *
+from _sre import MAXREPEAT
 
 SPECIAL_CHARS = ".\\[{()*+?^$|"
 REPEAT_CHARS = "*+?{"
@@ -228,7 +229,7 @@
     if code:
         return code
     code = CATEGORIES.get(escape)
-    if code:
+    if code and code[0] == IN:
         return code
     try:
         c = escape[1:2]
@@ -498,10 +499,14 @@
                     continue
                 if lo:
                     min = int(lo)
+                    if min >= MAXREPEAT:
+                        raise OverflowError("the repetition number is too large")
                 if hi:
                     max = int(hi)
-                if max < min:
-                    raise error, "bad repeat interval"
+                    if max >= MAXREPEAT:
+                        raise OverflowError("the repetition number is too large")
+                    if max < min:
+                        raise error("bad repeat interval")
             else:
                 raise error, "not supported"
             # figure out which item to repeat
@@ -541,6 +546,8 @@
                                 break
                             name = name + char
                         group = 1
+                        if not name:
+                            raise error("missing group name")
                         if not isname(name):
                             raise error, "bad character in group name"
                     elif sourcematch("="):
@@ -553,6 +560,8 @@
                             if char == ")":
                                 break
                             name = name + char
+                        if not name:
+                            raise error("missing group name")
                         if not isname(name):
                             raise error, "bad character in group name"
                         gid = state.groupdict.get(name)
@@ -605,6 +614,8 @@
                             break
                         condname = condname + char
                     group = 2
+                    if not condname:
+                        raise error("missing group name")
                     if isname(condname):
                         condgroup = state.groupdict.get(condname)
                         if condgroup is None:
@@ -723,7 +734,7 @@
                             break
                         name = name + char
                 if not name:
-                    raise error, "bad group name"
+                    raise error, "missing group name"
                 try:
                     index = int(name)
                     if index < 0:
diff --git a/lib-python/2.7/ssl.py b/lib-python/2.7/ssl.py
--- a/lib-python/2.7/ssl.py
+++ b/lib-python/2.7/ssl.py
@@ -313,17 +313,19 @@
                                     self.cert_reqs, self.ssl_version,
                                     self.ca_certs, self.ciphers)
         try:
-            socket.connect(self, addr)
-            if self.do_handshake_on_connect:
-                self.do_handshake()
-        except socket_error as e:
             if return_errno:
-                return e.errno
+                rc = socket.connect_ex(self, addr)
             else:
-                self._sslobj = None
-                raise e
-        self._connected = True
-        return 0
+                rc = None
+                socket.connect(self, addr)
+            if not rc:
+                if self.do_handshake_on_connect:
+                    self.do_handshake()
+                self._connected = True
+            return rc
+        except socket_error:
+            self._sslobj = None
+            raise
 
     def connect(self, addr):
         """Connects to remote ADDR, and then wraps the connection in
diff --git a/lib-python/2.7/string.py b/lib-python/2.7/string.py
--- a/lib-python/2.7/string.py
+++ b/lib-python/2.7/string.py
@@ -601,12 +601,12 @@
 
     def convert_field(self, value, conversion):
         # do any conversion on the resulting object
-        if conversion == 'r':
-            return repr(value)
+        if conversion is None:
+            return value
         elif conversion == 's':
             return str(value)
-        elif conversion is None:
-            return value
+        elif conversion == 'r':
+            return repr(value)
         raise ValueError("Unknown conversion specifier {0!s}".format(conversion))
 
 
diff --git a/lib-python/2.7/subprocess.py b/lib-python/2.7/subprocess.py
--- a/lib-python/2.7/subprocess.py
+++ b/lib-python/2.7/subprocess.py
@@ -671,12 +671,33 @@
          c2pread, c2pwrite,
          errread, errwrite) = self._get_handles(stdin, stdout, stderr)
 
-        self._execute_child(args, executable, preexec_fn, close_fds,
-                            cwd, env, universal_newlines,
-                            startupinfo, creationflags, shell,
-                            p2cread, p2cwrite,
-                            c2pread, c2pwrite,
-                            errread, errwrite)
+        try:
+            self._execute_child(args, executable, preexec_fn, close_fds,
+                                cwd, env, universal_newlines,
+                                startupinfo, creationflags, shell,
+                                p2cread, p2cwrite,
+                                c2pread, c2pwrite,
+                                errread, errwrite)
+        except Exception:
+            # Preserve original exception in case os.close raises.
+            exc_type, exc_value, exc_trace = sys.exc_info()
+
+            to_close = []
+            # Only close the pipes we created.
+            if stdin == PIPE:
+                to_close.extend((p2cread, p2cwrite))
+            if stdout == PIPE:
+                to_close.extend((c2pread, c2pwrite))
+            if stderr == PIPE:
+                to_close.extend((errread, errwrite))
+
+            for fd in to_close:
+                try:
+                    os.close(fd)
+                except EnvironmentError:
+                    pass
+
+            raise exc_type, exc_value, exc_trace
 
         if mswindows:
             if p2cwrite is not None:
@@ -1253,9 +1274,6 @@
                     if e.errno != errno.ECHILD:
                         raise
                 child_exception = pickle.loads(data)
-                for fd in (p2cwrite, c2pread, errread):
-                    if fd is not None:
-                        os.close(fd)
                 raise child_exception
 
 
@@ -1274,7 +1292,7 @@
 
 
         def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid,
-                _WNOHANG=os.WNOHANG, _os_error=os.error):
+                _WNOHANG=os.WNOHANG, _os_error=os.error, _ECHILD=errno.ECHILD):
             """Check if child process has terminated.  Returns returncode
             attribute.
 
@@ -1287,16 +1305,23 @@
                     pid, sts = _waitpid(self.pid, _WNOHANG)
                     if pid == self.pid:
                         self._handle_exitstatus(sts)
-                except _os_error:
+                except _os_error as e:
                     if _deadstate is not None:
                         self.returncode = _deadstate
+                    if e.errno == _ECHILD:
+                        # This happens if SIGCLD is set to be ignored or
+                        # waiting for child processes has otherwise been
+                        # disabled for our process.  This child is dead, we
+                        # can't get the status.
+                        # http://bugs.python.org/issue15756
+                        self.returncode = 0
             return self.returncode
 
 
         def wait(self):
             """Wait for child process to terminate.  Returns returncode
             attribute."""
-            if self.returncode is None:
+            while self.returncode is None:
                 try:
                     pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0)
                 except OSError as e:
@@ -1305,8 +1330,12 @@
                     # This happens if SIGCLD is set to be ignored or waiting
                     # for child processes has otherwise been disabled for our
                     # process.  This child is dead, we can't get the status.
+                    pid = self.pid
                     sts = 0
-                self._handle_exitstatus(sts)
+                # Check the pid and loop as waitpid has been known to return
+                # 0 even without WNOHANG in odd situations.  issue14396.
+                if pid == self.pid:
+                    self._handle_exitstatus(sts)
             return self.returncode
 
 
diff --git a/lib-python/2.7/sysconfig.py b/lib-python/2.7/sysconfig.py
--- a/lib-python/2.7/sysconfig.py
+++ b/lib-python/2.7/sysconfig.py
@@ -116,6 +116,10 @@
 if os.name == "nt" and "\\pcbuild\\amd64" in _PROJECT_BASE[-14:].lower():
     _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir))
 
+# set for cross builds
+if "_PYTHON_PROJECT_BASE" in os.environ:
+    # the build directory for posix builds
+    _PROJECT_BASE = os.path.normpath(os.path.abspath("."))
 def is_python_build():
     for fn in ("Setup.dist", "Setup.local"):
         if os.path.isfile(os.path.join(_PROJECT_BASE, "Modules", fn)):
@@ -445,64 +449,11 @@
                 srcdir = os.path.join(base, _CONFIG_VARS['srcdir'])
                 _CONFIG_VARS['srcdir'] = os.path.normpath(srcdir)
 
+        # OS X platforms require special customization to handle
+        # multi-architecture, multi-os-version installers
         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
+            import _osx_support
+            _osx_support.customize_config_vars(_CONFIG_VARS)
 
     if args:
         vals = []
@@ -560,6 +511,10 @@
             return 'win-ia64'
         return sys.platform
 
+    # Set for cross builds explicitly
+    if "_PYTHON_HOST_PLATFORM" in os.environ:
+        return os.environ["_PYTHON_HOST_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.
@@ -600,91 +555,10 @@
         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 1:
-            # 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())
-                    if m is not None:
-                        macrelease = '.'.join(m.group(1).split('.')[:2])
-                    # else: fall back to the default behaviour
-                finally:
-                    f.close()
-
-        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.maxint >= 2**32:
-                    machine = 'x86_64'
-
-            elif machine in ('PowerPC', 'Power_Macintosh'):
-                # Pick a sane name for the PPC architecture.
-                # See 'i386' case
-                if sys.maxint >= 2**32:
-                    machine = 'ppc64'
-                else:
-                    machine = 'ppc'
+        import _osx_support
+        osname, release, machine = _osx_support.get_platform_osx(
+                                            get_config_vars(),
+                                            osname, release, machine)
 
     return "%s-%s-%s" % (osname, release, machine)
 
diff --git a/lib-python/2.7/tarfile.py b/lib-python/2.7/tarfile.py
--- a/lib-python/2.7/tarfile.py
+++ b/lib-python/2.7/tarfile.py
@@ -1987,9 +1987,8 @@
 
         # Append the tar header and data to the archive.
         if tarinfo.isreg():
-            f = bltn_open(name, "rb")
-            self.addfile(tarinfo, f)
-            f.close()
+            with bltn_open(name, "rb") as f:
+                self.addfile(tarinfo, f)
 
         elif tarinfo.isdir():
             self.addfile(tarinfo)
@@ -2197,10 +2196,11 @@
         """Make a file called targetpath.
         """
         source = self.extractfile(tarinfo)
-        target = bltn_open(targetpath, "wb")
-        copyfileobj(source, target)
-        source.close()
-        target.close()
+        try:
+            with bltn_open(targetpath, "wb") as target:
+                copyfileobj(source, target)
+        finally:
+            source.close()
 
     def makeunknown(self, tarinfo, targetpath):
         """Make a file from a TarInfo object with an unknown type
@@ -2397,7 +2397,7 @@
         """
         if tarinfo.issym():
             # Always search the entire archive.
-            linkname = os.path.dirname(tarinfo.name) + "/" + tarinfo.linkname
+            linkname = "/".join(filter(None, (os.path.dirname(tarinfo.name), tarinfo.linkname)))
             limit = None
         else:
             # Search the archive before the link, because a hard link is
diff --git a/lib-python/2.7/telnetlib.py b/lib-python/2.7/telnetlib.py
--- a/lib-python/2.7/telnetlib.py
+++ b/lib-python/2.7/telnetlib.py
@@ -34,6 +34,7 @@
 
 
 # Imported modules
+import errno
 import sys
 import socket
 import select
@@ -205,6 +206,7 @@
         self.sb = 0 # flag for SB and SE sequence.
         self.sbdataq = ''
         self.option_callback = None
+        self._has_poll = hasattr(select, 'poll')
         if host is not None:
             self.open(host, port, timeout)
 
@@ -287,6 +289,61 @@
         is closed and no cooked data is available.
 
         """
+        if self._has_poll:
+            return self._read_until_with_poll(match, timeout)
+        else:
+            return self._read_until_with_select(match, timeout)
+
+    def _read_until_with_poll(self, match, timeout):
+        """Read until a given string is encountered or until timeout.
+
+        This method uses select.poll() to implement the timeout.
+        """
+        n = len(match)
+        call_timeout = timeout
+        if timeout is not None:
+            from time import time
+            time_start = time()
+        self.process_rawq()
+        i = self.cookedq.find(match)
+        if i < 0:
+            poller = select.poll()
+            poll_in_or_priority_flags = select.POLLIN | select.POLLPRI
+            poller.register(self, poll_in_or_priority_flags)
+            while i < 0 and not self.eof:
+                try:
+                    ready = poller.poll(call_timeout)
+                except select.error as e:
+                    if e.errno == errno.EINTR:
+                        if timeout is not None:
+                            elapsed = time() - time_start
+                            call_timeout = timeout-elapsed
+                        continue
+                    raise
+                for fd, mode in ready:
+                    if mode & poll_in_or_priority_flags:
+                        i = max(0, len(self.cookedq)-n)
+                        self.fill_rawq()
+                        self.process_rawq()
+                        i = self.cookedq.find(match, i)
+                if timeout is not None:
+                    elapsed = time() - time_start
+                    if elapsed >= timeout:
+                        break
+                    call_timeout = timeout-elapsed
+            poller.unregister(self)
+        if i >= 0:
+            i = i + n
+            buf = self.cookedq[:i]
+            self.cookedq = self.cookedq[i:]
+            return buf
+        return self.read_very_lazy()
+
+    def _read_until_with_select(self, match, timeout=None):
+        """Read until a given string is encountered or until timeout.
+
+        The timeout is implemented using select.select().
+        """
         n = len(match)
         self.process_rawq()
         i = self.cookedq.find(match)
@@ -589,6 +646,79 @@
         results are undeterministic, and may depend on the I/O timing.
 
         """
+        if self._has_poll:
+            return self._expect_with_poll(list, timeout)
+        else:
+            return self._expect_with_select(list, timeout)
+
+    def _expect_with_poll(self, expect_list, timeout=None):
+        """Read until one from a list of a regular expressions matches.
+
+        This method uses select.poll() to implement the timeout.
+        """
+        re = None
+        expect_list = expect_list[:]
+        indices = range(len(expect_list))
+        for i in indices:
+            if not hasattr(expect_list[i], "search"):
+                if not re: import re
+                expect_list[i] = re.compile(expect_list[i])
+        call_timeout = timeout
+        if timeout is not None:
+            from time import time
+            time_start = time()
+        self.process_rawq()
+        m = None
+        for i in indices:
+            m = expect_list[i].search(self.cookedq)
+            if m:
+                e = m.end()
+                text = self.cookedq[:e]
+                self.cookedq = self.cookedq[e:]
+                break
+        if not m:
+            poller = select.poll()
+            poll_in_or_priority_flags = select.POLLIN | select.POLLPRI
+            poller.register(self, poll_in_or_priority_flags)
+            while not m and not self.eof:
+                try:
+                    ready = poller.poll(call_timeout)
+                except select.error as e:
+                    if e.errno == errno.EINTR:
+                        if timeout is not None:
+                            elapsed = time() - time_start
+                            call_timeout = timeout-elapsed
+                        continue
+                    raise
+                for fd, mode in ready:
+                    if mode & poll_in_or_priority_flags:
+                        self.fill_rawq()
+                        self.process_rawq()
+                        for i in indices:
+                            m = expect_list[i].search(self.cookedq)
+                            if m:
+                                e = m.end()
+                                text = self.cookedq[:e]
+                                self.cookedq = self.cookedq[e:]
+                                break
+                if timeout is not None:
+                    elapsed = time() - time_start
+                    if elapsed >= timeout:
+                        break
+                    call_timeout = timeout-elapsed
+            poller.unregister(self)
+        if m:
+            return (i, m, text)
+        text = self.read_very_lazy()
+        if not text and self.eof:
+            raise EOFError
+        return (-1, None, text)
+
+    def _expect_with_select(self, list, timeout=None):
+        """Read until one from a list of a regular expressions matches.
+
+        The timeout is implemented using select.select().
+        """
         re = None
         list = list[:]
         indices = range(len(list))
diff --git a/lib-python/2.7/tempfile.py b/lib-python/2.7/tempfile.py
--- a/lib-python/2.7/tempfile.py
+++ b/lib-python/2.7/tempfile.py
@@ -29,6 +29,7 @@
 
 # Imports.
 
+import io as _io
 import os as _os
 import errno as _errno
 from random import Random as _Random
@@ -193,15 +194,18 @@
             name = namer.next()
             filename = _os.path.join(dir, name)
             try:
-                fd = _os.open(filename, flags, 0600)
-                fp = _os.fdopen(fd, 'w')
-                fp.write('blat')
-                fp.close()
-                _os.unlink(filename)
-                del fp, fd
+                fd = _os.open(filename, flags, 0o600)
+                try:
+                    try:
+                        with _io.open(fd, 'wb', closefd=False) as fp:
+                            fp.write(b'blat')
+                    finally:
+                        _os.close(fd)
+                finally:
+                    _os.unlink(filename)
                 return dir
-            except (OSError, IOError), e:
-                if e[0] != _errno.EEXIST:
+            except (OSError, IOError) as e:
+                if e.args[0] != _errno.EEXIST:
                     break # no point trying more names in this directory
                 pass
     raise IOError, (_errno.ENOENT,
@@ -546,10 +550,6 @@
     def closed(self):
         return self._file.closed
 
-    @property
-    def encoding(self):
-        return self._file.encoding
-
     def fileno(self):
         self.rollover()
         return self._file.fileno()
@@ -562,15 +562,17 @@
 
     @property
     def mode(self):
-        return self._file.mode
+        try:
+            return self._file.mode
+        except AttributeError:
+            return self._TemporaryFileArgs[0]
 
     @property
     def name(self):
-        return self._file.name
-
-    @property
-    def newlines(self):
-        return self._file.newlines
+        try:
+            return self._file.name
+        except AttributeError:
+            return None
 
     def next(self):
         return self._file.next
@@ -610,4 +612,7 @@
         return rv
 
     def xreadlines(self, *args):
-        return self._file.xreadlines(*args)
+        try:
+            return self._file.xreadlines(*args)
+        except AttributeError:
+            return iter(self._file.readlines(*args))
diff --git a/lib-python/2.7/test/crashers/buffer_mutate.py b/lib-python/2.7/test/crashers/buffer_mutate.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/test/crashers/buffer_mutate.py
@@ -0,0 +1,30 @@
+#
+# The various methods of bufferobject.c (here buffer_subscript()) call
+# get_buf() before calling potentially more Python code (here via
+# PySlice_GetIndicesEx()).  But get_buf() already returned a void*
+# pointer.  This void* pointer can become invalid if the object
+# underlying the buffer is mutated (here a bytearray object).
+#
+# As usual, please keep in mind that the three "here" in the sentence
+# above are only examples.  Each can be changed easily and lead to
+# another crasher.
+#
+# This crashes for me on Linux 32-bits with CPython 2.6 and 2.7
+# with a segmentation fault.
+#
+
+
+class PseudoIndex(object):
+    def __index__(self):
+        for c in "foobar"*n:
+            a.append(c)
+        return n * 4
+
+
+for n in range(1, 100000, 100):
+    a = bytearray("test"*n)
+    buf = buffer(a)
+
+    s = buf[:PseudoIndex():1]
+    #print repr(s)
+    #assert s == "test"*n
diff --git a/lib-python/2.7/test/keycert.pem b/lib-python/2.7/test/keycert.pem
--- a/lib-python/2.7/test/keycert.pem
+++ b/lib-python/2.7/test/keycert.pem
@@ -1,32 +1,31 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L
-opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH
-fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB
-AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU
-D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA
-IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM
-oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0
-ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/
-loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j
-oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA
-z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq
-ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV
-q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU=
------END RSA PRIVATE KEY-----
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANtb0+YrKuxevGpm
+LrjaUhZSgz6zFAmuGFmKmUbdjmfv9zSmmdsQIksK++jK0Be9LeZy20j6ahOfuVa0
+ufEmPoP7Fy4hXegKZR9cCWcIe/A6H2xWF1IIJLRTLaU8ol/I7T+um5HD5AwAwNPP
+USNU0Eegmvp+xxWu3NX2m1Veot85AgMBAAECgYA3ZdZ673X0oexFlq7AAmrutkHt
+CL7LvwrpOiaBjhyTxTeSNWzvtQBkIU8DOI0bIazA4UreAFffwtvEuPmonDb3F+Iq
+SMAu42XcGyVZEl+gHlTPU9XRX7nTOXVt+MlRRRxL6t9GkGfUAXI3XxJDXW3c0vBK
+UL9xqD8cORXOfE06rQJBAP8mEX1ERkR64Ptsoe4281vjTlNfIbs7NMPkUnrn9N/Y
+BLhjNIfQ3HFZG8BTMLfX7kCS9D593DW5tV4Z9BP/c6cCQQDcFzCcVArNh2JSywOQ
+ZfTfRbJg/Z5Lt9Fkngv1meeGNPgIMLN8Sg679pAOOWmzdMO3V706rNPzSVMME7E5
+oPIfAkEA8pDddarP5tCvTTgUpmTFbakm0KoTZm2+FzHcnA4jRh+XNTjTOv98Y6Ik
+eO5d1ZnKXseWvkZncQgxfdnMqqpj5wJAcNq/RVne1DbYlwWchT2Si65MYmmJ8t+F
+0mcsULqjOnEMwf5e+ptq5LzwbyrHZYq5FNk7ocufPv/ZQrcSSC+cFwJBAKvOJByS
+x56qyGeZLOQlWS2JS3KJo59XuLFGqcbgN9Om9xFa41Yb4N9NvplFivsvZdw3m1Q/
+SPIXQuT8RMPDVNQ=
+-----END PRIVATE KEY-----
 -----BEGIN CERTIFICATE-----
-MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD
-VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x
-IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT
-U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1
-NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl
-bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m
-dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj
-aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh
-m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8
-M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn
-fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC
-AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb
-08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx
-CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/
-iHkC6gGdBJhogs4=
+MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV
+BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u
+IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw
+MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH
+Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k
+YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7
+6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt
+pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw
+FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd
+BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G
+lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1
+CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX
 -----END CERTIFICATE-----
diff --git a/lib-python/2.7/test/mp_fork_bomb.py b/lib-python/2.7/test/mp_fork_bomb.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/test/mp_fork_bomb.py
@@ -0,0 +1,16 @@
+import multiprocessing
+
+def foo(conn):
+    conn.send("123")
+
+# Because "if __name__ == '__main__'" is missing this will not work
+# correctly on Windows.  However, we should get a RuntimeError rather
+# than the Windows equivalent of a fork bomb.
+
+r, w = multiprocessing.Pipe(False)
+p = multiprocessing.Process(target=foo, args=(w,))
+p.start()
+w.close()
+print(r.recv())
+r.close()
+p.join()
diff --git a/lib-python/2.7/test/pickletester.py b/lib-python/2.7/test/pickletester.py
--- a/lib-python/2.7/test/pickletester.py
+++ b/lib-python/2.7/test/pickletester.py
@@ -6,7 +6,8 @@
 import pickletools
 import copy_reg
 
-from test.test_support import TestFailed, have_unicode, TESTFN
+from test.test_support import (TestFailed, have_unicode, TESTFN, _2G, _1M,
+                               precisionbigmemtest)
 
 # Tests that try a number of pickle protocols should have a
 #     for proto in protocols:
@@ -502,10 +503,10 @@
         i = C()
         i.attr = i
         for proto in protocols:
-            s = self.dumps(i, 2)
+            s = self.dumps(i, proto)
             x = self.loads(s)
             self.assertEqual(dir(x), dir(i))
-            self.assertTrue(x.attr is x)
+            self.assertIs(x.attr, x)
 
     def test_recursive_multi(self):
         l = []
@@ -1280,3 +1281,31 @@
         f.write(pickled2)
         f.seek(0)
         self.assertEqual(unpickler.load(), data2)
+
+class BigmemPickleTests(unittest.TestCase):
+
+    # Memory requirements: 1 byte per character for input strings, 1 byte
+    # for pickled data, 1 byte for unpickled strings, 1 byte for internal
+    # buffer and 1 byte of free space for resizing of internal buffer.
+
+    @precisionbigmemtest(size=_2G + 100*_1M, memuse=5)
+    def test_huge_strlist(self, size):
+        chunksize = 2**20
+        data = []
+        while size > chunksize:
+            data.append('x' * chunksize)
+            size -= chunksize
+            chunksize += 1
+        data.append('y' * size)
+
+        try:
+            for proto in protocols:
+                try:
+                    pickled = self.dumps(data, proto)
+                    res = self.loads(pickled)
+                    self.assertEqual(res, data)
+                finally:
+                    res = None
+                    pickled = None
+        finally:
+            data = None
diff --git a/lib-python/2.7/test/regrtest.py b/lib-python/2.7/test/regrtest.py
--- a/lib-python/2.7/test/regrtest.py
+++ b/lib-python/2.7/test/regrtest.py
@@ -32,7 +32,7 @@
 
 Selecting tests
 
--r/--random     -- randomize test execution order (see below)
+-r/--randomize  -- randomize test execution order (see below)
    --randseed   -- pass a random seed to reproduce a previous random run
 -f/--fromfile   -- read names of tests to run from a file (see below)
 -x/--exclude    -- arguments are tests to *exclude*
@@ -158,6 +158,7 @@
 import os
 import random
 import re
+import shutil
 import sys
 import time
 import traceback
@@ -258,7 +259,7 @@
     try:
         opts, args = getopt.getopt(sys.argv[1:], 'hvqxsSrf:lu:t:TD:NLR:FwWM:j:',
             ['help', 'verbose', 'verbose2', 'verbose3', 'quiet',
-             'exclude', 'single', 'slow', 'random', 'fromfile', 'findleaks',
+             'exclude', 'single', 'slow', 'randomize', 'fromfile=', 'findleaks',
              'use=', 'threshold=', 'trace', 'coverdir=', 'nocoverdir',
              'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=',
              'multiprocess=', 'slaveargs=', 'forever', 'header'])
@@ -540,6 +541,8 @@
                     print stdout
                 if stderr:
                     print >>sys.stderr, stderr
+                sys.stdout.flush()
+                sys.stderr.flush()
                 if result[0] == INTERRUPTED:
                     assert result[1] == 'KeyboardInterrupt'
                     raise KeyboardInterrupt   # What else?
@@ -941,7 +944,6 @@
         return FAILED, test_time
 
 def cleanup_test_droppings(testname, verbose):
-    import shutil
     import stat
     import gc
 
diff --git a/lib-python/2.7/test/sample_doctest_no_docstrings.py b/lib-python/2.7/test/sample_doctest_no_docstrings.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/test/sample_doctest_no_docstrings.py
@@ -0,0 +1,12 @@
+# This is a sample module used for testing doctest.
+#
+# This module is for testing how doctest handles a module with no
+# docstrings.
+
+
+class Foo(object):
+
+    # A class with no docstring.
+
+    def __init__(self):
+        pass
diff --git a/lib-python/2.7/test/sample_doctest_no_doctests.py b/lib-python/2.7/test/sample_doctest_no_doctests.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/test/sample_doctest_no_doctests.py
@@ -0,0 +1,15 @@
+"""This is a sample module used for testing doctest.
+
+This module is for testing how doctest handles a module with docstrings
+but no doctest examples.
+
+"""
+
+
+class Foo(object):
+    """A docstring with no doctest examples.
+
+    """
+
+    def __init__(self):
+        pass
diff --git a/lib-python/2.7/test/script_helper.py b/lib-python/2.7/test/script_helper.py
--- a/lib-python/2.7/test/script_helper.py
+++ b/lib-python/2.7/test/script_helper.py
@@ -10,7 +10,13 @@
 import py_compile
 import contextlib
 import shutil
-import zipfile
+try:
+    import zipfile
+except ImportError:
+    # If Python is build without Unicode support, importing _io will
+    # fail, which, in turn, means that zipfile cannot be imported
+    # Most of this module can then still be used.
+    pass
 
 from test.test_support import strip_python_stderr
 
diff --git a/lib-python/2.7/test/sha256.pem b/lib-python/2.7/test/sha256.pem
--- a/lib-python/2.7/test/sha256.pem
+++ b/lib-python/2.7/test/sha256.pem
@@ -1,129 +1,128 @@
 # Certificate chain for https://sha256.tbs-internet.com
- 0 s:/C=FR/postalCode=14000/ST=Calvados/L=CAEN/street=22 rue de Bretagne/O=TBS INTERNET/OU=0002 440443810/OU=sha-256 production/CN=sha256.tbs-internet.com
-   i:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC
+ 0 s:/C=FR/postalCode=14000/ST=Calvados/L=CAEN/street=22 rue de Bretagne/O=TBS INTERNET/OU=0002 440443810/OU=Certificats TBS X509/CN=ecom.tbs-x509.com
+   i:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA business
 -----BEGIN CERTIFICATE-----
-MIIGXTCCBUWgAwIBAgIRAMmag+ygSAdxZsbyzYjhuW0wDQYJKoZIhvcNAQELBQAw
-gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl
+MIIGTjCCBTagAwIBAgIQOh3d9dNDPq1cSdJmEiMpqDANBgkqhkiG9w0BAQUFADCB
+yTELMAkGA1UEBhMCRlIxETAPBgNVBAgTCENhbHZhZG9zMQ0wCwYDVQQHEwRDYWVu
+MRUwEwYDVQQKEwxUQlMgSU5URVJORVQxSDBGBgNVBAsTP1Rlcm1zIGFuZCBDb25k
+aXRpb25zOiBodHRwOi8vd3d3LnRicy1pbnRlcm5ldC5jb20vQ0EvcmVwb3NpdG9y
+eTEYMBYGA1UECxMPVEJTIElOVEVSTkVUIENBMR0wGwYDVQQDExRUQlMgWDUwOSBD
+QSBidXNpbmVzczAeFw0xMTAxMjUwMDAwMDBaFw0xMzAyMDUyMzU5NTlaMIHHMQsw
+CQYDVQQGEwJGUjEOMAwGA1UEERMFMTQwMDAxETAPBgNVBAgTCENhbHZhZG9zMQ0w
+CwYDVQQHEwRDQUVOMRswGQYDVQQJExIyMiBydWUgZGUgQnJldGFnbmUxFTATBgNV
+BAoTDFRCUyBJTlRFUk5FVDEXMBUGA1UECxMOMDAwMiA0NDA0NDM4MTAxHTAbBgNV
+BAsTFENlcnRpZmljYXRzIFRCUyBYNTA5MRowGAYDVQQDExFlY29tLnRicy14NTA5
+LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKRrlHUnJ++1lpcg
+jtYco7cdmRe+EEfTmwPfCdfV3G1QfsTSvY6FfMpm/83pqHfT+4ANwr18wD9ZrAEN
+G16mf9VdCGK12+TP7DmqeZyGIqlFFoahQnmb8EarvE43/1UeQ2CV9XmzwZvpqeli
+LfXsFonawrY3H6ZnMwS64St61Z+9gdyuZ/RbsoZBbT5KUjDEG844QRU4OT1IGeEI
+eY5NM5RNIh6ZNhVtqeeCxMS7afONkHQrOco73RdSTRck/Hj96Ofl3MHNHryr+AMK
+DGFk1kLCZGpPdXtkxXvaDeQoiYDlil26CWc+YK6xyDPMdsWvoG14ZLyCpzMXA7/7
+4YAQRH0CAwEAAaOCAjAwggIsMB8GA1UdIwQYMBaAFBoJBMz5CY+7HqDO1KQUf0vV
+I1jNMB0GA1UdDgQWBBQgOU8HsWzbmD4WZP5Wtdw7jca2WDAOBgNVHQ8BAf8EBAMC
+BaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
+TAYDVR0gBEUwQzBBBgsrBgEEAYDlNwIBATAyMDAGCCsGAQUFBwIBFiRodHRwczov
+L3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL0NQUzEwdwYDVR0fBHAwbjA3oDWgM4Yx
+aHR0cDovL2NybC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQWJ1c2luZXNzLmNy
+bDAzoDGgL4YtaHR0cDovL2NybC50YnMteDUwOS5jb20vVEJTWDUwOUNBYnVzaW5l
+c3MuY3JsMIGwBggrBgEFBQcBAQSBozCBoDA9BggrBgEFBQcwAoYxaHR0cDovL2Ny
+dC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQWJ1c2luZXNzLmNydDA5BggrBgEF
+BQcwAoYtaHR0cDovL2NydC50YnMteDUwOS5jb20vVEJTWDUwOUNBYnVzaW5lc3Mu
+Y3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC50YnMteDUwOS5jb20wMwYDVR0R
+BCwwKoIRZWNvbS50YnMteDUwOS5jb22CFXd3dy5lY29tLnRicy14NTA5LmNvbTAN
+BgkqhkiG9w0BAQUFAAOCAQEArT4NHfbY87bGAw8lPV4DmHlmuDuVp/y7ltO3Ynse
+3Rz8RxW2AzuO0Oy2F0Cu4yWKtMyEyMXyHqWtae7ElRbdTu5w5GwVBLJHClCzC8S9
+SpgMMQTx3Rgn8vjkHuU9VZQlulZyiPK7yunjc7c310S9FRZ7XxOwf8Nnx4WnB+No
+WrfApzhhQl31w+RyrNxZe58hCfDDHmevRvwLjQ785ZoQXJDj2j3qAD4aI2yB8lB5
+oaE1jlCJzC7Kmz/Y9jzfmv/zAs1LQTm9ktevv4BTUFaGjv9jxnQ1xnS862ZiouLW
+zZYIlYPf4F6JjXGiIQgQRglILUfq3ftJd9/ok9W9ZF8h8w==
+-----END CERTIFICATE-----
+ 1 s:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA business
+   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
+-----BEGIN CERTIFICATE-----
+MIIFPzCCBCegAwIBAgIQDlBz/++iRSmLDeVRHT/hADANBgkqhkiG9w0BAQUFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTA1MTIwMTAwMDAwMFoXDTE5MDcwOTE4MTkyMlow
+gckxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl
 bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u
 ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv
-cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg
-Q0EgU0dDMB4XDTEwMDIxODAwMDAwMFoXDTEyMDIxOTIzNTk1OVowgcsxCzAJBgNV
-BAYTAkZSMQ4wDAYDVQQREwUxNDAwMDERMA8GA1UECBMIQ2FsdmFkb3MxDTALBgNV
-BAcTBENBRU4xGzAZBgNVBAkTEjIyIHJ1ZSBkZSBCcmV0YWduZTEVMBMGA1UEChMM
-VEJTIElOVEVSTkVUMRcwFQYDVQQLEw4wMDAyIDQ0MDQ0MzgxMDEbMBkGA1UECxMS
-c2hhLTI1NiBwcm9kdWN0aW9uMSAwHgYDVQQDExdzaGEyNTYudGJzLWludGVybmV0
-LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbuM8VT7f0nntwu
-N3F7v9KIBlhKNAxqCrziOXU5iqUt8HrQB3DtHbdmII+CpVUlwlmepsx6G+srEZ9a
-MIGAy0nxi5aLb7watkyIdPjJTMvTUBQ/+RPWzt5JtYbbY9BlJ+yci0dctP74f4NU
-ISLtlrEjUbf2gTohLrcE01TfmOF6PDEbB5PKDi38cB3NzKfizWfrOaJW6Q1C1qOJ
-y4/4jkUREX1UFUIxzx7v62VfjXSGlcjGpBX1fvtABQOSLeE0a6gciDZs1REqroFf
-5eXtqYphpTa14Z83ITXMfgg5Nze1VtMnzI9Qx4blYBw4dgQVEuIsYr7FDBOITDzc
-VEVXZx0CAwEAAaOCAj8wggI7MB8GA1UdIwQYMBaAFAdEdoWTKLx/bXjSCuv6TEvf
-2YIfMB0GA1UdDgQWBBSJKI/AYVI9RQNY0QPIqc8ej2QivTAOBgNVHQ8BAf8EBAMC
-BaAwDAYDVR0TAQH/BAIwADA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIG
-CisGAQQBgjcKAwMGCWCGSAGG+EIEATBMBgNVHSAERTBDMEEGCysGAQQBgOU3AgQB
-MDIwMAYIKwYBBQUHAgEWJGh0dHBzOi8vd3d3LnRicy1pbnRlcm5ldC5jb20vQ0Ev
-Q1BTNDBtBgNVHR8EZjBkMDKgMKAuhixodHRwOi8vY3JsLnRicy1pbnRlcm5ldC5j
-b20vVEJTWDUwOUNBU0dDLmNybDAuoCygKoYoaHR0cDovL2NybC50YnMteDUwOS5j
-b20vVEJTWDUwOUNBU0dDLmNybDCBpgYIKwYBBQUHAQEEgZkwgZYwOAYIKwYBBQUH
-MAKGLGh0dHA6Ly9jcnQudGJzLWludGVybmV0LmNvbS9UQlNYNTA5Q0FTR0MuY3J0
-MDQGCCsGAQUFBzAChihodHRwOi8vY3J0LnRicy14NTA5LmNvbS9UQlNYNTA5Q0FT
-R0MuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC50YnMteDUwOS5jb20wPwYD
-VR0RBDgwNoIXc2hhMjU2LnRicy1pbnRlcm5ldC5jb22CG3d3dy5zaGEyNTYudGJz
-LWludGVybmV0LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAA5NL0D4QSqhErhlkdPmz
-XtiMvdGL+ZehM4coTRIpasM/Agt36Rc0NzCvnQwKE+wkngg1Gy2qe7Q0E/ziqBtB
-fZYzdVgu1zdiL4kTaf+wFKYAFGsFbyeEmXysy+CMwaNoF2vpSjCU1UD56bEnTX/W
-fxVZYxtBQUpnu2wOsm8cDZuZRv9XrYgAhGj9Tt6F0aVHSDGn59uwShG1+BVF/uju
-SCyPTTjL1oc7YElJUzR/x4mQJYvtQI8gDIDAGEOs7v3R/gKa5EMfbUQUI4C84UbI
-Yz09Jdnws/MkC/Hm1BZEqk89u7Hvfv+oHqEb0XaUo0TDfsxE0M1sMdnLb91QNQBm
-UQ==
------END CERTIFICATE-----
- 1 s:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC
-   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
------BEGIN CERTIFICATE-----
-MIIFVjCCBD6gAwIBAgIQXpDZ0ETJMV02WTx3GTnhhTANBgkqhkiG9w0BAQUFADBv
-MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
-ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
-eHRlcm5hbCBDQSBSb290MB4XDTA1MTIwMTAwMDAwMFoXDTE5MDYyNDE5MDYzMFow
-gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl
-bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u
-ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv
-cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg
-Q0EgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgOkO3f7wzN6
-rOjg45tR5vjBfzK7qmV9IBxb/QW9EEXxG+E7FNhZqQLtwGBKoSsHTnQqV75wWMk0
-9tinWvftBkSpj5sTi/8cbzJfUvTSVYh3Qxv6AVVjMMH/ruLjE6y+4PoaPs8WoYAQ
-ts5R4Z1g8c/WnTepLst2x0/Wv7GmuoQi+gXvHU6YrBiu7XkeYhzc95QdviWSJRDk
-owhb5K43qhcvjRmBfO/paGlCliDGZp8mHwrI21mwobWpVjTxZRwYO3bd4+TGcI4G
-Ie5wmHwE8F7SK1tgSqbBacKjDa93j7txKkfz/Yd2n7TGqOXiHPsJpG655vrKtnXk
-9vs1zoDeJQIDAQABo4IBljCCAZIwHQYDVR0OBBYEFAdEdoWTKLx/bXjSCuv6TEvf
-2YIfMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMCAGA1UdJQQZ
-MBcGCisGAQQBgjcKAwMGCWCGSAGG+EIEATAYBgNVHSAEETAPMA0GCysGAQQBgOU3
-AgQBMHsGA1UdHwR0MHIwOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0Fk
-ZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMDagNKAyhjBodHRwOi8vY3JsLmNvbW9k
-by5uZXQvQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5jcmwwgYAGCCsGAQUFBwEBBHQw
-cjA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21vZG9jYS5jb20vQWRkVHJ1c3RV
-VE5TR0NDQS5jcnQwNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvLm5ldC9B
-ZGRUcnVzdFVUTlNHQ0NBLmNydDARBglghkgBhvhCAQEEBAMCAgQwDQYJKoZIhvcN
-AQEFBQADggEBAK2zEzs+jcIrVK9oDkdDZNvhuBYTdCfpxfFs+OAujW0bIfJAy232
-euVsnJm6u/+OrqKudD2tad2BbejLLXhMZViaCmK7D9nrXHx4te5EP8rL19SUVqLY
-1pTnv5dhNgEgvA7n5lIzDSYs7yRLsr7HJsYPr6SeYSuZizyX1SNz7ooJ32/F3X98
-RB0Mlc/E0OyOrkQ9/y5IrnpnaSora8CnUrV5XNOg+kyCz9edCyx4D5wXYcwZPVWz
-8aDqquESrezPyjtfi4WRO4s/VD3HLZvOxzMrWAVYCDG9FxaOhF0QGuuG1F7F3GKV
-v6prNyCl016kRl2j1UT+a7gLd8fA25A4C9E=
+cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEdMBsGA1UEAxMUVEJTIFg1MDkg
+Q0EgYnVzaW5lc3MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB1PAU
+qudCcz3tmyGcf+u6EkZqonKKHrV4gZYbvVkIRojmmlhfi/jwvpHvo8bqSt/9Rj5S
+jhCDW0pcbI+IPPtD1Jy+CHNSfnMqVDy6CKQ3p5maTzCMG6ZT+XjnvcND5v+FtaiB
+xk1iCX6uvt0jeUtdZvYbyytsSDE6c3Y5//wRxOF8tM1JxibwO3pyER26jbbN2gQz
+m/EkdGjLdJ4svPk23WDAvQ6G0/z2LcAaJB+XLfqRwfQpHQvfKa1uTi8PivC8qtip
+rmNQMMPMjxSK2azX8cKjjTDJiUKaCb4VHlJDWKEsCFRpgJAoAuX8f7Yfs1M4esGo
+sWb3PGspK3O22uIlAgMBAAGjggF6MIIBdjAdBgNVHQ4EFgQUGgkEzPkJj7seoM7U
+pBR/S9UjWM0wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwGAYD
+VR0gBBEwDzANBgsrBgEEAYDlNwIBATB7BgNVHR8EdDByMDigNqA0hjJodHRwOi8v
+Y3JsLmNvbW9kb2NhLmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDA2oDSg
+MoYwaHR0cDovL2NybC5jb21vZG8ubmV0L0FkZFRydXN0RXh0ZXJuYWxDQVJvb3Qu
+Y3JsMIGGBggrBgEFBQcBAQR6MHgwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29t
+b2RvY2EuY29tL0FkZFRydXN0VVROU2VydmVyQ0EuY3J0MDkGCCsGAQUFBzAChi1o
+dHRwOi8vY3J0LmNvbW9kby5uZXQvQWRkVHJ1c3RVVE5TZXJ2ZXJDQS5jcnQwEQYJ
+YIZIAYb4QgEBBAQDAgIEMA0GCSqGSIb3DQEBBQUAA4IBAQA7mqrMgk/MrE6QnbNA
+h4nRCn2ti4bg4w2C3lB6bSvRPnYwuNw9Jb8vuKkNFzRDxNJXqVDZdfFW5CVQJuyd
+nfAx83+wk+spzvFaE1KhFYfN9G9pQfXUfvDRoIcJgPEKUXL1wRiOG+IjU3VVI8pg
+IgqHkr7ylln5i5zCiFAPuIJmYUSFg/gxH5xkCNcjJqqrHrHatJr6Qrrke93joupw
+oU1njfAcZtYp6fbiK6u2b1pJqwkVBE8RsfLnPhRj+SFbpvjv8Od7o/ieJhFIYQNU
+k2jX2u8qZnAiNw93LZW9lpYjtuvMXq8QQppENNja5b53q7UwI+lU7ZGjZ7quuESp
+J6/5
 -----END CERTIFICATE-----
  2 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
-   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC
+   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware
 -----BEGIN CERTIFICATE-----
-MIIEZjCCA06gAwIBAgIQUSYKkxzif5zDpV954HKugjANBgkqhkiG9w0BAQUFADCB
-kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+MIIETzCCAzegAwIBAgIQHM5EYpUZep1jUvnyI6m2mDANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
 Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
-IFNHQzAeFw0wNTA2MDcwODA5MTBaFw0xOTA2MjQxOTA2MzBaMG8xCzAJBgNVBAYT
-AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0
-ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB
-IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC39xoz5vIABC05
-4E5b7R+8bA/Ntfojts7emxEzl6QpTH2Tn71KvJPtAxrjj8/lbVBa1pcplFqAsEl6
-2y6V/bjKvzc4LR4+kUGtcFbH8E8/6DKedMrIkFTpxl8PeJ2aQDwOrGGqXhSPnoeh
-alDc15pOrwWzpnGUnHGzUGAKxxOdOAeGAqjpqGkmGJCrTLBPI6s6T4TY386f4Wlv
-u9dC12tE5Met7m1BX3JacQg3s3llpFmglDf3AC8NwpJy2tA4ctsUqEXEXSp9t7TW
-xO6szRNEt8kr3UMAJfphuWlqWCMRt6czj1Z1WfXNKddGtworZbbTQm8Vsrh7++/p
-XVPVNFonAgMBAAGjgdgwgdUwHwYDVR0jBBgwFoAUUzLRs89/+uDxoF2FTpLSnkUd
-tE8wHQYDVR0OBBYEFK29mHo0tCb3+sQmVO8DveAky1QaMA4GA1UdDwEB/wQEAwIB
-BjAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBAjAgBgNVHSUEGTAX
-BgorBgEEAYI3CgMDBglghkgBhvhCBAEwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDov
-L2NybC51c2VydHJ1c3QuY29tL1VUTi1EQVRBQ29ycFNHQy5jcmwwDQYJKoZIhvcN
-AQEFBQADggEBAMbuUxdoFLJRIh6QWA2U/b3xcOWGLcM2MY9USEbnLQg3vGwKYOEO
-rVE04BKT6b64q7gmtOmWPSiPrmQH/uAB7MXjkesYoPF1ftsK5p+R26+udd8jkWjd
-FwBaS/9kbHDrARrQkNnHptZt9hPk/7XJ0h4qy7ElQyZ42TCbTg0evmnv3+r+LbPM
-+bDdtRTKkdSytaX7ARmjR3mfnYyVhzT4HziS2jamEfpr62vp3EV4FTkG101B5CHI
-3C+H0be/SGB1pWLLJN47YaApIKa+xWycxOkKaSLvkTr6Jq/RW0GnOuL4OAdCq8Fb
-+M5tug8EPzI0rNwEKNdwMBQmBsTkm5jVz3g=
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNMDUwNjA3MDgwOTEwWhcNMTkwNzA5MTgxOTIyWjBvMQswCQYD
+VQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0
+IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5h
+bCBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt/caM+by
+AAQtOeBOW+0fvGwPzbX6I7bO3psRM5ekKUx9k5+9SryT7QMa44/P5W1QWtaXKZRa
+gLBJetsulf24yr83OC0ePpFBrXBWx/BPP+gynnTKyJBU6cZfD3idmkA8Dqxhql4U
+j56HoWpQ3NeaTq8Fs6ZxlJxxs1BgCscTnTgHhgKo6ahpJhiQq0ywTyOrOk+E2N/O
+n+Fpb7vXQtdrROTHre5tQV9yWnEIN7N5ZaRZoJQ39wAvDcKSctrQOHLbFKhFxF0q
+fbe01sTurM0TRLfJK91DACX6YblpalgjEbenM49WdVn1zSnXRrcKK2W200JvFbK4
+e/vv6V1T1TRaJwIDAQABo4G9MIG6MB8GA1UdIwQYMBaAFKFyXyYbKJhDlV0HN9WF
+lp1L0sNFMB0GA1UdDgQWBBStvZh6NLQm9/rEJlTvA73gJMtUGjAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAQIwRAYDVR0f
+BD0wOzA5oDegNYYzaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmly
+c3QtSGFyZHdhcmUuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQByQhANOs4kClrwF8BW
+onvUOGCSjRK52zYZgDXYNjDtmr5rJ6NyPFDNn+JxkLpjYetIFMTbSRe679Bt8m7a
+gIAoQYFQtxMuyLnJegB2aEbQiIxh/tC21UcFF7ktdnDoTlA6w3pLuvunaI84Of3o
+2YBrhzkTbCfaYk5JRlTpudW9DkUkHBsyx3nknPKnplkIGaK0jgn8E0n+SFabYaHk
+I9LroYT/+JtLefh9lgBdAgVv0UPbzoGfuDsrk/Zh+UrgbLFpHoVnElhzbkh64Z0X
+OGaJunQc68cCZu5HTn/aK7fBGMcVflRCXLVEQpU9PIAdGA8Ynvg684t8GMaKsRl1
+jIGZ
 -----END CERTIFICATE-----
- 3 s:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC
-   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC
+ 3 s:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware
+   i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware
 -----BEGIN CERTIFICATE-----
-MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB
-kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
+lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
 Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
-dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
-IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG
-EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
-VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu
-dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN
-BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6
-E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ
-D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK
-4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq
-lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW
-bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB
-o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
-MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js
-LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr
-BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB
-AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
-Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj
-j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH
-KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv
-2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3
-mfnGV/TJVTl4uix5yaaIK/QI
+dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
+SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
+A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
+MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
+d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
+cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
+0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
+M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
+MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
+oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
+DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
+oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
+VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
+dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
+bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
+BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
+//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
+CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
+CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
+3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
+KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==
 -----END CERTIFICATE-----
diff --git a/lib-python/2.7/test/string_tests.py b/lib-python/2.7/test/string_tests.py
--- a/lib-python/2.7/test/string_tests.py
+++ b/lib-python/2.7/test/string_tests.py
@@ -5,6 +5,7 @@
 import unittest, string, sys, struct
 from test import test_support
 from UserList import UserList
+import _testcapi
 
 class Sequence:
     def __init__(self, seq='wxyz'): self.seq = seq
@@ -1113,6 +1114,23 @@
         self.checkraises(TypeError, '%10.*f', '__mod__', ('foo', 42.))
         self.checkraises(ValueError, '%10', '__mod__', (42,))
 
+        width = int(_testcapi.PY_SSIZE_T_MAX + 1)
+        if width <= sys.maxint:
+            self.checkraises(OverflowError, '%*s', '__mod__', (width, ''))
+        prec = int(_testcapi.INT_MAX + 1)
+        if prec <= sys.maxint:
+            self.checkraises(OverflowError, '%.*f', '__mod__', (prec, 1. / 7))
+        # Issue 15989
+        width = int(1 << (_testcapi.PY_SSIZE_T_MAX.bit_length() + 1))
+        if width <= sys.maxint:
+            self.checkraises(OverflowError, '%*s', '__mod__', (width, ''))
+        prec = int(_testcapi.UINT_MAX + 1)
+        if prec <= sys.maxint:
+            self.checkraises(OverflowError, '%.*f', '__mod__', (prec, 1. / 7))
+
+        class X(object): pass
+        self.checkraises(TypeError, 'abc', '__mod__', X())
+
     def test_floatformatting(self):
         # float formatting
         for prec in xrange(100):
diff --git a/lib-python/2.7/test/subprocessdata/sigchild_ignore.py b/lib-python/2.7/test/subprocessdata/sigchild_ignore.py
--- a/lib-python/2.7/test/subprocessdata/sigchild_ignore.py
+++ b/lib-python/2.7/test/subprocessdata/sigchild_ignore.py
@@ -1,6 +1,15 @@
-import signal, subprocess, sys
+import signal, subprocess, sys, time
 # On Linux this causes os.waitpid to fail with OSError as the OS has already
 # reaped our child process.  The wait() passing the OSError on to the caller
 # and causing us to exit with an error is what we are testing against.
 signal.signal(signal.SIGCHLD, signal.SIG_IGN)
 subprocess.Popen([sys.executable, '-c', 'print("albatross")']).wait()
+# Also ensure poll() handles an errno.ECHILD appropriately.
+p = subprocess.Popen([sys.executable, '-c', 'print("albatross")'])
+num_polls = 0
+while p.poll() is None:
+    # Waiting for the process to finish.
+    time.sleep(0.01)  # Avoid being a CPU busy loop.
+    num_polls += 1
+    if num_polls > 3000:
+        raise RuntimeError('poll should have returned 0 within 30 seconds')
diff --git a/lib-python/2.7/test/test_StringIO.py b/lib-python/2.7/test/test_StringIO.py
--- a/lib-python/2.7/test/test_StringIO.py
+++ b/lib-python/2.7/test/test_StringIO.py
@@ -5,6 +5,7 @@
 import cStringIO
 import types
 import array
+import sys
 from test import test_support
 
 
@@ -27,6 +28,8 @@
         eq = self.assertEqual
         self.assertRaises(TypeError, self._fp.seek)
         eq(self._fp.read(10), self._line[:10])
+        eq(self._fp.read(0), '')
+        eq(self._fp.readline(0), '')
         eq(self._fp.readline(), self._line[10:] + '\n')
         eq(len(self._fp.readlines(60)), 2)
         self._fp.seek(0)
@@ -105,6 +108,45 @@
         self._fp.close()
         self.assertRaises(ValueError, self._fp.getvalue)
 
+    @test_support.bigmemtest(test_support._2G + 2**26, memuse=2.001)
+    def test_reads_from_large_stream(self, size):
+        linesize = 2**26 # 64 MiB
+        lines = ['x' * (linesize - 1) + '\n'] * (size // linesize) + \
+                ['y' * (size % linesize)]
+        f = self.MODULE.StringIO(''.join(lines))
+        for i, expected in enumerate(lines):
+            line = f.read(len(expected))
+            self.assertEqual(len(line), len(expected))
+            self.assertEqual(line, expected)
+        self.assertEqual(f.read(), '')
+        f.seek(0)
+        for i, expected in enumerate(lines):
+            line = f.readline()
+            self.assertEqual(len(line), len(expected))
+            self.assertEqual(line, expected)
+        self.assertEqual(f.readline(), '')
+        f.seek(0)
+        self.assertEqual(f.readlines(), lines)
+        self.assertEqual(f.readlines(), [])
+        f.seek(0)
+        self.assertEqual(f.readlines(size), lines)
+        self.assertEqual(f.readlines(), [])
+
+    # In worst case cStringIO requires 2 + 1 + 1/2 + 1/2**2 + ... = 4
+    # bytes per input character.
+    @test_support.bigmemtest(test_support._2G, memuse=4)
+    def test_writes_to_large_stream(self, size):
+        s = 'x' * 2**26 # 64 MiB
+        f = self.MODULE.StringIO()
+        n = size
+        while n > len(s):
+            f.write(s)
+            n -= len(s)
+        s = None
+        f.write('x' * n)
+        self.assertEqual(len(f.getvalue()), size)
+
+
 class TestStringIO(TestGenericStringIO):
     MODULE = StringIO
 
diff --git a/lib-python/2.7/test/test__osx_support.py b/lib-python/2.7/test/test__osx_support.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/test/test__osx_support.py
@@ -0,0 +1,279 @@
+"""
+Test suite for _osx_support: shared OS X support functions.
+"""
+
+import os
+import platform
+import shutil
+import stat
+import sys
+import unittest
+
+import test.test_support
+
+import _osx_support
+
+ at unittest.skipUnless(sys.platform.startswith("darwin"), "requires OS X")
+class Test_OSXSupport(unittest.TestCase):
+
+    def setUp(self):
+        self.maxDiff = None
+        self.prog_name = 'bogus_program_xxxx'
+        self.temp_path_dir = os.path.abspath(os.getcwd())
+        self.env = test.test_support.EnvironmentVarGuard()
+        self.addCleanup(self.env.__exit__)
+        for cv in ('CFLAGS', 'LDFLAGS', 'CPPFLAGS',
+                            'BASECFLAGS', 'BLDSHARED', 'LDSHARED', 'CC',
+                            'CXX', 'PY_CFLAGS', 'PY_LDFLAGS', 'PY_CPPFLAGS',
+                            'PY_CORE_CFLAGS'):
+            if cv in self.env:
+                self.env.unset(cv)
+
+    def add_expected_saved_initial_values(self, config_vars, expected_vars):
+        # Ensure that the initial values for all modified config vars
+        # are also saved with modified keys.
+        expected_vars.update(('_OSX_SUPPORT_INITIAL_'+ k,
+                config_vars[k]) for k in config_vars
+                    if config_vars[k] != expected_vars[k])
+
+    def test__find_executable(self):
+        if self.env['PATH']:
+            self.env['PATH'] = self.env['PATH'] + ':'
+        self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir)
+        test.test_support.unlink(self.prog_name)
+        self.assertIsNone(_osx_support._find_executable(self.prog_name))
+        self.addCleanup(test.test_support.unlink, self.prog_name)
+        with open(self.prog_name, 'w') as f:
+            f.write("#!/bin/sh\n/bin/echo OK\n")
+        os.chmod(self.prog_name, stat.S_IRWXU)
+        self.assertEqual(self.prog_name,
+                            _osx_support._find_executable(self.prog_name))
+
+    def test__read_output(self):
+        if self.env['PATH']:
+            self.env['PATH'] = self.env['PATH'] + ':'
+        self.env['PATH'] = self.env['PATH'] + os.path.abspath(self.temp_path_dir)
+        test.test_support.unlink(self.prog_name)
+        self.addCleanup(test.test_support.unlink, self.prog_name)
+        with open(self.prog_name, 'w') as f:
+            f.write("#!/bin/sh\n/bin/echo ExpectedOutput\n")
+        os.chmod(self.prog_name, stat.S_IRWXU)
+        self.assertEqual('ExpectedOutput',
+                            _osx_support._read_output(self.prog_name))
+
+    def test__find_build_tool(self):
+        out = _osx_support._find_build_tool('cc')
+        self.assertTrue(os.path.isfile(out),
+                            'cc not found - check xcode-select')
+
+    def test__get_system_version(self):
+        self.assertTrue(platform.mac_ver()[0].startswith(
+                                    _osx_support._get_system_version()))
+
+    def test__remove_original_values(self):
+        config_vars = {
+        'CC': 'gcc-test -pthreads',
+        }
+        expected_vars = {
+        'CC': 'clang -pthreads',
+        }
+        cv = 'CC'
+        newvalue = 'clang -pthreads'
+        _osx_support._save_modified_value(config_vars, cv, newvalue)
+        self.assertNotEqual(expected_vars, config_vars)
+        _osx_support._remove_original_values(config_vars)
+        self.assertEqual(expected_vars, config_vars)
+
+    def test__save_modified_value(self):
+        config_vars = {
+        'CC': 'gcc-test -pthreads',
+        }
+        expected_vars = {
+        'CC': 'clang -pthreads',
+        }
+        self.add_expected_saved_initial_values(config_vars, expected_vars)
+        cv = 'CC'
+        newvalue = 'clang -pthreads'
+        _osx_support._save_modified_value(config_vars, cv, newvalue)
+        self.assertEqual(expected_vars, config_vars)
+
+    def test__save_modified_value_unchanged(self):
+        config_vars = {
+        'CC': 'gcc-test -pthreads',
+        }
+        expected_vars = config_vars.copy()
+        cv = 'CC'
+        newvalue = 'gcc-test -pthreads'
+        _osx_support._save_modified_value(config_vars, cv, newvalue)
+        self.assertEqual(expected_vars, config_vars)
+
+    def test__supports_universal_builds(self):
+        import platform
+        self.assertEqual(platform.mac_ver()[0].split('.') >= ['10', '4'],
+                            _osx_support._supports_universal_builds())
+
+    def test__find_appropriate_compiler(self):
+        compilers = (
+                        ('gcc-test', 'i686-apple-darwin11-llvm-gcc-4.2'),
+                        ('clang', 'clang version 3.1'),
+                    )
+        config_vars = {
+        'CC': 'gcc-test -pthreads',
+        'CXX': 'cc++-test',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
+        'LDFLAGS': '-arch ppc -arch i386   -g',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
+        'BLDSHARED': 'gcc-test -bundle -arch ppc -arch i386 -g',
+        'LDSHARED': 'gcc-test -bundle -arch ppc -arch i386 '
+                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
+        }
+        expected_vars = {
+        'CC': 'clang -pthreads',
+        'CXX': 'clang++',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
+        'LDFLAGS': '-arch ppc -arch i386   -g',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
+        'BLDSHARED': 'clang -bundle -arch ppc -arch i386 -g',
+        'LDSHARED': 'clang -bundle -arch ppc -arch i386 '
+                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
+        }
+        self.add_expected_saved_initial_values(config_vars, expected_vars)
+
+        suffix = (':' + self.env['PATH']) if self.env['PATH'] else ''
+        self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix
+        for c_name, c_output in compilers:
+            test.test_support.unlink(c_name)
+            self.addCleanup(test.test_support.unlink, c_name)
+            with open(c_name, 'w') as f:
+                f.write("#!/bin/sh\n/bin/echo " + c_output)
+            os.chmod(c_name, stat.S_IRWXU)
+        self.assertEqual(expected_vars,
+                            _osx_support._find_appropriate_compiler(
+                                    config_vars))
+
+    def test__remove_universal_flags(self):
+        config_vars = {
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
+        'LDFLAGS': '-arch ppc -arch i386   -g',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
+        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
+        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
+                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
+        }
+        expected_vars = {
+        'CFLAGS': '-fno-strict-aliasing  -g -O3    ',
+        'LDFLAGS': '    -g',
+        'CPPFLAGS': '-I.  ',
+        'BLDSHARED': 'gcc-4.0 -bundle    -g',
+        'LDSHARED': 'gcc-4.0 -bundle      -g',
+        }
+        self.add_expected_saved_initial_values(config_vars, expected_vars)
+
+        self.assertEqual(expected_vars,
+                            _osx_support._remove_universal_flags(
+                                    config_vars))
+
+    def test__remove_unsupported_archs(self):
+        config_vars = {
+        'CC': 'clang',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
+        'LDFLAGS': '-arch ppc -arch i386   -g',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
+        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
+        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
+                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
+        }
+        expected_vars = {
+        'CC': 'clang',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3  -arch i386  ',
+        'LDFLAGS': ' -arch i386   -g',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
+        'BLDSHARED': 'gcc-4.0 -bundle   -arch i386 -g',
+        'LDSHARED': 'gcc-4.0 -bundle   -arch i386 '
+                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
+        }
+        self.add_expected_saved_initial_values(config_vars, expected_vars)
+
+        suffix = (':' + self.env['PATH']) if self.env['PATH'] else ''
+        self.env['PATH'] = os.path.abspath(self.temp_path_dir) + suffix
+        c_name = 'clang'
+        test.test_support.unlink(c_name)
+        self.addCleanup(test.test_support.unlink, c_name)
+        # exit status 255 means no PPC support in this compiler chain
+        with open(c_name, 'w') as f:
+            f.write("#!/bin/sh\nexit 255")
+        os.chmod(c_name, stat.S_IRWXU)
+        self.assertEqual(expected_vars,
+                            _osx_support._remove_unsupported_archs(
+                                    config_vars))
+
+    def test__override_all_archs(self):
+        self.env['ARCHFLAGS'] = '-arch x86_64'
+        config_vars = {
+        'CC': 'clang',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  ',
+        'LDFLAGS': '-arch ppc -arch i386   -g',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
+        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
+        'LDSHARED': 'gcc-4.0 -bundle -arch ppc -arch i386 '
+                        '-isysroot /Developer/SDKs/MacOSX10.4u.sdk -g',
+        }
+        expected_vars = {
+        'CC': 'clang',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3     -arch x86_64',
+        'LDFLAGS': '    -g -arch x86_64',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.4u.sdk',
+        'BLDSHARED': 'gcc-4.0 -bundle    -g -arch x86_64',
+        'LDSHARED': 'gcc-4.0 -bundle   -isysroot '
+                        '/Developer/SDKs/MacOSX10.4u.sdk -g -arch x86_64',
+        }
+        self.add_expected_saved_initial_values(config_vars, expected_vars)
+
+        self.assertEqual(expected_vars,
+                            _osx_support._override_all_archs(
+                                    config_vars))
+
+    def test__check_for_unavailable_sdk(self):
+        config_vars = {
+        'CC': 'clang',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  '
+                        '-isysroot /Developer/SDKs/MacOSX10.1.sdk',
+        'LDFLAGS': '-arch ppc -arch i386   -g',
+        'CPPFLAGS': '-I. -isysroot /Developer/SDKs/MacOSX10.1.sdk',
+        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
+        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
+                        '-isysroot /Developer/SDKs/MacOSX10.1.sdk -g',
+        }
+        expected_vars = {
+        'CC': 'clang',
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  '
+                        ' ',
+        'LDFLAGS': '-arch ppc -arch i386   -g',
+        'CPPFLAGS': '-I.  ',
+        'BLDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 -g',
+        'LDSHARED': 'gcc-4.0 -bundle  -arch ppc -arch i386 '
+                        ' -g',
+        }
+        self.add_expected_saved_initial_values(config_vars, expected_vars)
+
+        self.assertEqual(expected_vars,
+                            _osx_support._check_for_unavailable_sdk(
+                                    config_vars))
+
+    def test_get_platform_osx(self):
+        # Note, get_platform_osx is currently tested more extensively
+        # indirectly by test_sysconfig and test_distutils
+        config_vars = {
+        'CFLAGS': '-fno-strict-aliasing  -g -O3 -arch ppc -arch i386  '
+                        '-isysroot /Developer/SDKs/MacOSX10.1.sdk',
+        'MACOSX_DEPLOYMENT_TARGET': '10.6',
+        }
+        result = _osx_support.get_platform_osx(config_vars, ' ', ' ', ' ')
+        self.assertEqual(('macosx', '10.6', 'fat'), result)
+
+def test_main():
+    if sys.platform == 'darwin':
+        test.test_support.run_unittest(Test_OSXSupport)
+
+if __name__ == "__main__":
+    test_main()
diff --git a/lib-python/2.7/test/test_aifc.py b/lib-python/2.7/test/test_aifc.py
--- a/lib-python/2.7/test/test_aifc.py
+++ b/lib-python/2.7/test/test_aifc.py
@@ -106,6 +106,13 @@
         self.assertEqual(testfile.closed, False)
         f.close()
         self.assertEqual(testfile.closed, True)
+        testfile = open(TESTFN, 'wb')
+        fout = aifc.open(testfile, 'wb')
+        self.assertFalse(testfile.closed)
+        with self.assertRaises(aifc.Error):
+            fout.close()
+        self.assertTrue(testfile.closed)
+        fout.close() # do nothing
 
 
 class AIFCLowLevelTest(unittest.TestCase):
diff --git a/lib-python/2.7/test/test_argparse.py b/lib-python/2.7/test/test_argparse.py
--- a/lib-python/2.7/test/test_argparse.py
+++ b/lib-python/2.7/test/test_argparse.py
@@ -1374,6 +1374,7 @@
         ('X @hello', NS(a=None, x='X', y=['hello world!'])),
         ('-a B @recursive Y Z', NS(a='A', x='hello world!', y=['Y', 'Z'])),
         ('X @recursive Z -a B', NS(a='B', x='X', y=['hello world!', 'Z'])),
+        (["-a", "", "X", "Y"], NS(a='', x='X', y=['Y'])),
     ]
 
 
@@ -1466,6 +1467,22 @@
         ('readonly', NS(x=None, spam=RFile('readonly'))),
     ]
 
+class TestFileTypeDefaults(TempDirMixin, ParserTestCase):
+    """Test that a file is not created unless the default is needed"""
+    def setUp(self):
+        super(TestFileTypeDefaults, self).setUp()
+        file = open(os.path.join(self.temp_dir, 'good'), 'w')
+        file.write('good')
+        file.close()
+
+    argument_signatures = [
+        Sig('-c', type=argparse.FileType('r'), default='no-file.txt'),
+    ]
+    # should provoke no such file error
+    failures = ['']
+    # should not provoke error because default file is created
+    successes = [('-c good', NS(c=RFile('good')))]
+
 
 class TestFileTypeRB(TempDirMixin, ParserTestCase):
     """Test the FileType option/argument type for reading files"""
@@ -1763,6 +1780,14 @@
         parser2.add_argument('-y', choices='123', help='y help')
         parser2.add_argument('z', type=complex, nargs='*', help='z help')
 
+        # add third sub-parser
+        parser3_kwargs = dict(description='3 description')
+        if subparser_help:
+            parser3_kwargs['help'] = '3 help'
+        parser3 = subparsers.add_parser('3', **parser3_kwargs)
+        parser3.add_argument('t', type=int, help='t help')
+        parser3.add_argument('u', nargs='...', help='u help')
+
         # return the main parser
         return parser
 
@@ -1792,6 +1817,10 @@
             self.parser.parse_args('--foo 0.125 1 c'.split()),
             NS(foo=True, bar=0.125, w=None, x='c'),
         )
+        self.assertEqual(
+            self.parser.parse_args('-1.5 3 11 -- a --foo 7 -- b'.split()),
+            NS(foo=False, bar=-1.5, t=11, u=['a', '--foo', '7', '--', 'b']),
+        )
 
     def test_parse_known_args(self):
         self.assertEqual(
@@ -1826,15 +1855,15 @@
 
     def test_help(self):
         self.assertEqual(self.parser.format_usage(),
-                         'usage: PROG [-h] [--foo] bar {1,2} ...\n')
+                         'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')
         self.assertEqual(self.parser.format_help(), textwrap.dedent('''\
-            usage: PROG [-h] [--foo] bar {1,2} ...
+            usage: PROG [-h] [--foo] bar {1,2,3} ...
 
             main description
 
             positional arguments:
               bar         bar help
-              {1,2}       command help
+              {1,2,3}     command help
 
             optional arguments:
               -h, --help  show this help message and exit
@@ -1845,15 +1874,15 @@
         # Make sure - is still used for help if it is a non-first prefix char
         parser = self._get_parser(prefix_chars='+:-')
         self.assertEqual(parser.format_usage(),
-                         'usage: PROG [-h] [++foo] bar {1,2} ...\n')
+                         'usage: PROG [-h] [++foo] bar {1,2,3} ...\n')
         self.assertEqual(parser.format_help(), textwrap.dedent('''\
-            usage: PROG [-h] [++foo] bar {1,2} ...
+            usage: PROG [-h] [++foo] bar {1,2,3} ...
 
             main description
 
             positional arguments:
               bar         bar help
-              {1,2}       command help
+              {1,2,3}     command help
 
             optional arguments:
               -h, --help  show this help message and exit
@@ -1864,15 +1893,15 @@
     def test_help_alternate_prefix_chars(self):
         parser = self._get_parser(prefix_chars='+:/')
         self.assertEqual(parser.format_usage(),
-                         'usage: PROG [+h] [++foo] bar {1,2} ...\n')
+                         'usage: PROG [+h] [++foo] bar {1,2,3} ...\n')
         self.assertEqual(parser.format_help(), textwrap.dedent('''\
-            usage: PROG [+h] [++foo] bar {1,2} ...
+            usage: PROG [+h] [++foo] bar {1,2,3} ...
 
             main description
 
             positional arguments:
               bar         bar help
-              {1,2}       command help
+              {1,2,3}     command help
 
             optional arguments:
               +h, ++help  show this help message and exit
@@ -1881,18 +1910,19 @@
 
     def test_parser_command_help(self):
         self.assertEqual(self.command_help_parser.format_usage(),
-                         'usage: PROG [-h] [--foo] bar {1,2} ...\n')
+                         'usage: PROG [-h] [--foo] bar {1,2,3} ...\n')
         self.assertEqual(self.command_help_parser.format_help(),
                          textwrap.dedent('''\
-            usage: PROG [-h] [--foo] bar {1,2} ...
+            usage: PROG [-h] [--foo] bar {1,2,3} ...
 
             main description
 
             positional arguments:
               bar         bar help
-              {1,2}       command help
+              {1,2,3}     command help
                 1         1 help
                 2         2 help
+                3         3 help
 
             optional arguments:
               -h, --help  show this help message and exit
@@ -4418,12 +4448,95 @@
         else:
             self.fail()
 
+# ================================================
+# Check that the type function is called only once
+# ================================================
+
+class TestTypeFunctionCallOnlyOnce(TestCase):
+
+    def test_type_function_call_only_once(self):
+        def spam(string_to_convert):
+            self.assertEqual(string_to_convert, 'spam!')
+            return 'foo_converted'
+
+        parser = argparse.ArgumentParser()
+        parser.add_argument('--foo', type=spam, default='bar')
+        args = parser.parse_args('--foo spam!'.split())
+        self.assertEqual(NS(foo='foo_converted'), args)
+
+# ==================================================================
+# Check semantics regarding the default argument and type conversion
+# ==================================================================
+
+class TestTypeFunctionCalledOnDefault(TestCase):
+
+    def test_type_function_call_with_non_string_default(self):
+        def spam(int_to_convert):
+            self.assertEqual(int_to_convert, 0)
+            return 'foo_converted'
+
+        parser = argparse.ArgumentParser()
+        parser.add_argument('--foo', type=spam, default=0)
+        args = parser.parse_args([])
+        # foo should *not* be converted because its default is not a string.
+        self.assertEqual(NS(foo=0), args)
+
+    def test_type_function_call_with_string_default(self):
+        def spam(int_to_convert):
+            return 'foo_converted'
+
+        parser = argparse.ArgumentParser()
+        parser.add_argument('--foo', type=spam, default='0')
+        args = parser.parse_args([])
+        # foo is converted because its default is a string.
+        self.assertEqual(NS(foo='foo_converted'), args)
+
+    def test_no_double_type_conversion_of_default(self):
+        def extend(str_to_convert):
+            return str_to_convert + '*'
+
+        parser = argparse.ArgumentParser()
+        parser.add_argument('--test', type=extend, default='*')
+        args = parser.parse_args([])
+        # The test argument will be two stars, one coming from the default
+        # value and one coming from the type conversion being called exactly
+        # once.
+        self.assertEqual(NS(test='**'), args)
+
+    def test_issue_15906(self):
+        # Issue #15906: When action='append', type=str, default=[] are
+        # providing, the dest value was the string representation "[]" when it
+        # should have been an empty list.
+        parser = argparse.ArgumentParser()
+        parser.add_argument('--test', dest='test', type=str,
+                            default=[], action='append')
+        args = parser.parse_args([])
+        self.assertEqual(args.test, [])
+
 # ======================
 # parse_known_args tests
 # ======================
 
 class TestParseKnownArgs(TestCase):
 
+    def test_arguments_tuple(self):
+        parser = argparse.ArgumentParser()
+        parser.parse_args(())
+
+    def test_arguments_list(self):
+        parser = argparse.ArgumentParser()
+        parser.parse_args([])
+
+    def test_arguments_tuple_positional(self):
+        parser = argparse.ArgumentParser()
+        parser.add_argument('x')
+        parser.parse_args(('x',))
+
+    def test_arguments_list_positional(self):
+        parser = argparse.ArgumentParser()
+        parser.add_argument('x')
+        parser.parse_args(['x'])
+
     def test_optionals(self):
         parser = argparse.ArgumentParser()
         parser.add_argument('--foo')
diff --git a/lib-python/2.7/test/test_array.py b/lib-python/2.7/test/test_array.py
--- a/lib-python/2.7/test/test_array.py
+++ b/lib-python/2.7/test/test_array.py
@@ -985,6 +985,19 @@
         upper = long(pow(2, a.itemsize * 8)) - 1L
         self.check_overflow(lower, upper)
 
+    @test_support.cpython_only
+    def test_sizeof_with_buffer(self):
+        a = array.array(self.typecode, self.example)
+        basesize = test_support.calcvobjsize('4P')
+        buffer_size = a.buffer_info()[1] * a.itemsize
+        test_support.check_sizeof(self, a, basesize + buffer_size)
+
+    @test_support.cpython_only
+    def test_sizeof_without_buffer(self):
+        a = array.array(self.typecode)
+        basesize = test_support.calcvobjsize('4P')
+        test_support.check_sizeof(self, a, basesize)
+
 
 class ByteTest(SignedNumberTest):
     typecode = 'b'
diff --git a/lib-python/2.7/test/test_ast.py b/lib-python/2.7/test/test_ast.py
--- a/lib-python/2.7/test/test_ast.py
+++ b/lib-python/2.7/test/test_ast.py
@@ -231,6 +231,12 @@
         im = ast.parse("from . import y").body[0]
         self.assertIsNone(im.module)
 
+    def test_non_interned_future_from_ast(self):
+        mod = ast.parse("from __future__ import division")
+        self.assertIsInstance(mod.body[0], ast.ImportFrom)
+        mod.body[0].module = " __future__ ".strip()
+        compile(mod, "<test>", "exec")
+
     def test_base_classes(self):
         self.assertTrue(issubclass(ast.For, ast.stmt))
         self.assertTrue(issubclass(ast.Name, ast.expr))
diff --git a/lib-python/2.7/test/test_asyncore.py b/lib-python/2.7/test/test_asyncore.py
--- a/lib-python/2.7/test/test_asyncore.py
+++ b/lib-python/2.7/test/test_asyncore.py
@@ -7,6 +7,7 @@
 import time
 import warnings
 import errno
+import struct
 
 from test import test_support
 from test.test_support import TESTFN, run_unittest, unlink
@@ -483,8 +484,9 @@
         return self.socket.getsockname()[:2]
 
     def handle_accept(self):
-        sock, addr = self.accept()
-        self.handler(sock)
+        pair = self.accept()
+        if pair is not None:
+            self.handler(pair[0])
 
     def handle_error(self):
         raise
@@ -703,6 +705,27 @@
         finally:
             sock.close()
 
+    @unittest.skipUnless(threading, 'Threading required for this test.')
+    @test_support.reap_threads
+    def test_quick_connect(self):
+        # see: http://bugs.python.org/issue10340
+        server = TCPServer()
+        t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, count=500))
+        t.start()
+        self.addCleanup(t.join)
+
+        for x in xrange(20):
+            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            s.settimeout(.2)
+            s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+                         struct.pack('ii', 1, 0))
+            try:
+                s.connect(server.address)
+            except socket.error:
+                pass
+            finally:
+                s.close()
+
 
 class TestAPI_UseSelect(BaseTestAPI):
     use_poll = False
diff --git a/lib-python/2.7/test/test_audioop.py b/lib-python/2.7/test/test_audioop.py
--- a/lib-python/2.7/test/test_audioop.py
+++ b/lib-python/2.7/test/test_audioop.py
@@ -1,25 +1,33 @@
 import audioop
+import sys
 import unittest
+import struct
 from test.test_support import run_unittest
 
-endian = 'big' if audioop.getsample('\0\1', 2, 0) == 1 else 'little'
 
-def gendata1():
-    return '\0\1\2'
+formats = {
+    1: 'b',
+    2: 'h',
+    4: 'i',
+}
 
-def gendata2():
-    if endian == 'big':
-        return '\0\0\0\1\0\2'
-    else:
-        return '\0\0\1\0\2\0'
+def pack(width, data):
+    return struct.pack('=%d%s' % (len(data), formats[width]), *data)
 
-def gendata4():
-    if endian == 'big':
-        return '\0\0\0\0\0\0\0\1\0\0\0\2'
-    else:
-        return '\0\0\0\0\1\0\0\0\2\0\0\0'
+packs = {
+    1: lambda *data: pack(1, data),
+    2: lambda *data: pack(2, data),
+    4: lambda *data: pack(4, data),
+}
+maxvalues = {w: (1 << (8 * w - 1)) - 1 for w in (1, 2, 4)}
+minvalues = {w: -1 << (8 * w - 1) for w in (1, 2, 4)}
 
-data = [gendata1(), gendata2(), gendata4()]
+datas = {
+    1: b'\x00\x12\x45\xbb\x7f\x80\xff',
+    2: packs[2](0, 0x1234, 0x4567, -0x4567, 0x7fff, -0x8000, -1),
+    4: packs[4](0, 0x12345678, 0x456789ab, -0x456789ab,
+                0x7fffffff, -0x80000000, -1),
+}
 
 INVALID_DATA = [
     (b'abc', 0),
@@ -31,164 +39,315 @@
 class TestAudioop(unittest.TestCase):
 
     def test_max(self):
-        self.assertEqual(audioop.max(data[0], 1), 2)
-        self.assertEqual(audioop.max(data[1], 2), 2)
-        self.assertEqual(audioop.max(data[2], 4), 2)
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.max(b'', w), 0)
+            p = packs[w]
+            self.assertEqual(audioop.max(p(5), w), 5)
+            self.assertEqual(audioop.max(p(5, -8, -1), w), 8)
+            self.assertEqual(audioop.max(p(maxvalues[w]), w), maxvalues[w])
+            self.assertEqual(audioop.max(p(minvalues[w]), w), -minvalues[w])
+            self.assertEqual(audioop.max(datas[w], w), -minvalues[w])
 
     def test_minmax(self):
-        self.assertEqual(audioop.minmax(data[0], 1), (0, 2))
-        self.assertEqual(audioop.minmax(data[1], 2), (0, 2))
-        self.assertEqual(audioop.minmax(data[2], 4), (0, 2))
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.minmax(b'', w),
+                             (0x7fffffff, -0x80000000))
+            p = packs[w]
+            self.assertEqual(audioop.minmax(p(5), w), (5, 5))
+            self.assertEqual(audioop.minmax(p(5, -8, -1), w), (-8, 5))
+            self.assertEqual(audioop.minmax(p(maxvalues[w]), w),
+                             (maxvalues[w], maxvalues[w]))
+            self.assertEqual(audioop.minmax(p(minvalues[w]), w),
+                             (minvalues[w], minvalues[w]))
+            self.assertEqual(audioop.minmax(datas[w], w),
+                             (minvalues[w], maxvalues[w]))
 
     def test_maxpp(self):
-        self.assertEqual(audioop.maxpp(data[0], 1), 0)
-        self.assertEqual(audioop.maxpp(data[1], 2), 0)
-        self.assertEqual(audioop.maxpp(data[2], 4), 0)
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.maxpp(b'', w), 0)
+            self.assertEqual(audioop.maxpp(packs[w](*range(100)), w), 0)
+            self.assertEqual(audioop.maxpp(packs[w](9, 10, 5, 5, 0, 1), w), 10)
+            self.assertEqual(audioop.maxpp(datas[w], w),
+                             maxvalues[w] - minvalues[w])
 
     def test_avg(self):
-        self.assertEqual(audioop.avg(data[0], 1), 1)
-        self.assertEqual(audioop.avg(data[1], 2), 1)
-        self.assertEqual(audioop.avg(data[2], 4), 1)
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.avg(b'', w), 0)
+            p = packs[w]
+            self.assertEqual(audioop.avg(p(5), w), 5)
+            self .assertEqual(audioop.avg(p(5, 8), w), 6)
+            self.assertEqual(audioop.avg(p(5, -8), w), -2)
+            self.assertEqual(audioop.avg(p(maxvalues[w], maxvalues[w]), w),
+                             maxvalues[w])
+            self.assertEqual(audioop.avg(p(minvalues[w], minvalues[w]), w),
+                             minvalues[w])
+        self.assertEqual(audioop.avg(packs[4](0x50000000, 0x70000000), 4),
+                         0x60000000)
+        self.assertEqual(audioop.avg(packs[4](-0x50000000, -0x70000000), 4),
+                         -0x60000000)
 
     def test_avgpp(self):
-        self.assertEqual(audioop.avgpp(data[0], 1), 0)
-        self.assertEqual(audioop.avgpp(data[1], 2), 0)
-        self.assertEqual(audioop.avgpp(data[2], 4), 0)
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.avgpp(b'', w), 0)
+            self.assertEqual(audioop.avgpp(packs[w](*range(100)), w), 0)
+            self.assertEqual(audioop.avgpp(packs[w](9, 10, 5, 5, 0, 1), w), 10)
+        self.assertEqual(audioop.avgpp(datas[1], 1), 196)
+        self.assertEqual(audioop.avgpp(datas[2], 2), 50534)
+        self.assertEqual(audioop.avgpp(datas[4], 4), 3311897002)
 
     def test_rms(self):
-        self.assertEqual(audioop.rms(data[0], 1), 1)
-        self.assertEqual(audioop.rms(data[1], 2), 1)
-        self.assertEqual(audioop.rms(data[2], 4), 1)
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.rms(b'', w), 0)
+            p = packs[w]
+            self.assertEqual(audioop.rms(p(*range(100)), w), 57)
+            self.assertAlmostEqual(audioop.rms(p(maxvalues[w]) * 5, w),
+                                   maxvalues[w], delta=1)
+            self.assertAlmostEqual(audioop.rms(p(minvalues[w]) * 5, w),
+                                   -minvalues[w], delta=1)
+        self.assertEqual(audioop.rms(datas[1], 1), 77)
+        self.assertEqual(audioop.rms(datas[2], 2), 20001)
+        self.assertEqual(audioop.rms(datas[4], 4), 1310854152)
 
     def test_cross(self):
-        self.assertEqual(audioop.cross(data[0], 1), 0)
-        self.assertEqual(audioop.cross(data[1], 2), 0)
-        self.assertEqual(audioop.cross(data[2], 4), 0)
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.cross(b'', w), -1)
+            p = packs[w]
+            self.assertEqual(audioop.cross(p(0, 1, 2), w), 0)
+            self.assertEqual(audioop.cross(p(1, 2, -3, -4), w), 1)
+            self.assertEqual(audioop.cross(p(-1, -2, 3, 4), w), 1)
+            self.assertEqual(audioop.cross(p(0, minvalues[w]), w), 1)
+            self.assertEqual(audioop.cross(p(minvalues[w], maxvalues[w]), w), 1)
 
     def test_add(self):
-        data2 = []
-        for d in data:
-            str = ''
-            for s in d:
-                str = str + chr(ord(s)*2)
-            data2.append(str)
-        self.assertEqual(audioop.add(data[0], data[0], 1), data2[0])
-        self.assertEqual(audioop.add(data[1], data[1], 2), data2[1])
-        self.assertEqual(audioop.add(data[2], data[2], 4), data2[2])
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.add(b'', b'', w), b'')
+            self.assertEqual(audioop.add(datas[w], b'\0' * len(datas[w]), w),
+                             datas[w])
+        self.assertEqual(audioop.add(datas[1], datas[1], 1),
+                         b'\x00\x24\x7f\x80\x7f\x80\xfe')
+        self.assertEqual(audioop.add(datas[2], datas[2], 2),
+                packs[2](0, 0x2468, 0x7fff, -0x8000, 0x7fff, -0x8000, -2))
+        self.assertEqual(audioop.add(datas[4], datas[4], 4),
+                packs[4](0, 0x2468acf0, 0x7fffffff, -0x80000000,
+                       0x7fffffff, -0x80000000, -2))
 
     def test_bias(self):
-        # Note: this test assumes that avg() works
-        d1 = audioop.bias(data[0], 1, 100)
-        d2 = audioop.bias(data[1], 2, 100)
-        d4 = audioop.bias(data[2], 4, 100)
-        self.assertEqual(audioop.avg(d1, 1), 101)
-        self.assertEqual(audioop.avg(d2, 2), 101)
-        self.assertEqual(audioop.avg(d4, 4), 101)
+        for w in 1, 2, 4:
+            for bias in 0, 1, -1, 127, -128, 0x7fffffff, -0x80000000:
+                self.assertEqual(audioop.bias(b'', w, bias), b'')
+        self.assertEqual(audioop.bias(datas[1], 1, 1),
+                         b'\x01\x13\x46\xbc\x80\x81\x00')
+        self.assertEqual(audioop.bias(datas[1], 1, -1),
+                         b'\xff\x11\x44\xba\x7e\x7f\xfe')
+        self.assertEqual(audioop.bias(datas[1], 1, 0x7fffffff),
+                         b'\xff\x11\x44\xba\x7e\x7f\xfe')
+        self.assertEqual(audioop.bias(datas[1], 1, -0x80000000),
+                         datas[1])
+        self.assertEqual(audioop.bias(datas[2], 2, 1),
+                packs[2](1, 0x1235, 0x4568, -0x4566, -0x8000, -0x7fff, 0))
+        self.assertEqual(audioop.bias(datas[2], 2, -1),
+                packs[2](-1, 0x1233, 0x4566, -0x4568, 0x7ffe, 0x7fff, -2))
+        self.assertEqual(audioop.bias(datas[2], 2, 0x7fffffff),
+                packs[2](-1, 0x1233, 0x4566, -0x4568, 0x7ffe, 0x7fff, -2))
+        self.assertEqual(audioop.bias(datas[2], 2, -0x80000000),
+                datas[2])
+        self.assertEqual(audioop.bias(datas[4], 4, 1),
+                packs[4](1, 0x12345679, 0x456789ac, -0x456789aa,
+                         -0x80000000, -0x7fffffff, 0))
+        self.assertEqual(audioop.bias(datas[4], 4, -1),
+                packs[4](-1, 0x12345677, 0x456789aa, -0x456789ac,
+                         0x7ffffffe, 0x7fffffff, -2))
+        self.assertEqual(audioop.bias(datas[4], 4, 0x7fffffff),
+                packs[4](0x7fffffff, -0x6dcba989, -0x3a987656, 0x3a987654,
+                         -2, -1, 0x7ffffffe))
+        self.assertEqual(audioop.bias(datas[4], 4, -0x80000000),
+                packs[4](-0x80000000, -0x6dcba988, -0x3a987655, 0x3a987655,
+                         -1, 0, 0x7fffffff))
 
     def test_lin2lin(self):
-        # too simple: we test only the size
-        for d1 in data:
-            for d2 in data:
-                got = len(d1)//3
-                wtd = len(d2)//3
-                self.assertEqual(len(audioop.lin2lin(d1, got, wtd)), len(d2))
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.lin2lin(datas[w], w, w), datas[w])
+
+        self.assertEqual(audioop.lin2lin(datas[1], 1, 2),
+            packs[2](0, 0x1200, 0x4500, -0x4500, 0x7f00, -0x8000, -0x100))
+        self.assertEqual(audioop.lin2lin(datas[1], 1, 4),
+            packs[4](0, 0x12000000, 0x45000000, -0x45000000,
+                     0x7f000000, -0x80000000, -0x1000000))
+        self.assertEqual(audioop.lin2lin(datas[2], 2, 1),
+            b'\x00\x12\x45\xba\x7f\x80\xff')
+        self.assertEqual(audioop.lin2lin(datas[2], 2, 4),
+            packs[4](0, 0x12340000, 0x45670000, -0x45670000,
+                     0x7fff0000, -0x80000000, -0x10000))
+        self.assertEqual(audioop.lin2lin(datas[4], 4, 1),
+            b'\x00\x12\x45\xba\x7f\x80\xff')
+        self.assertEqual(audioop.lin2lin(datas[4], 4, 2),
+            packs[2](0, 0x1234, 0x4567, -0x4568, 0x7fff, -0x8000, -1))
 
     def test_adpcm2lin(self):
+        self.assertEqual(audioop.adpcm2lin(b'\x07\x7f\x7f', 1, None),
+                         (b'\x00\x00\x00\xff\x00\xff', (-179, 40)))
+        self.assertEqual(audioop.adpcm2lin(b'\x07\x7f\x7f', 2, None),
+                         (packs[2](0, 0xb, 0x29, -0x16, 0x72, -0xb3), (-179, 40)))
+        self.assertEqual(audioop.adpcm2lin(b'\x07\x7f\x7f', 4, None),
+                         (packs[4](0, 0xb0000, 0x290000, -0x160000, 0x720000,
+                                   -0xb30000), (-179, 40)))
+
         # Very cursory test
-        self.assertEqual(audioop.adpcm2lin(b'\0\0', 1, None), (b'\0' * 4, (0,0)))
-        self.assertEqual(audioop.adpcm2lin(b'\0\0', 2, None), (b'\0' * 8, (0,0)))
-        self.assertEqual(audioop.adpcm2lin(b'\0\0', 4, None), (b'\0' * 16, (0,0)))
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.adpcm2lin(b'\0' * 5, w, None),
+                             (b'\0' * w * 10, (0, 0)))
 
     def test_lin2adpcm(self):
+        self.assertEqual(audioop.lin2adpcm(datas[1], 1, None),
+                         (b'\x07\x7f\x7f', (-221, 39)))
+        self.assertEqual(audioop.lin2adpcm(datas[2], 2, None),
+                         (b'\x07\x7f\x7f', (31, 39)))
+        self.assertEqual(audioop.lin2adpcm(datas[4], 4, None),
+                         (b'\x07\x7f\x7f', (31, 39)))
+
         # Very cursory test
-        self.assertEqual(audioop.lin2adpcm('\0\0\0\0', 1, None), ('\0\0', (0,0)))
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.lin2adpcm(b'\0' * w * 10, w, None),
+                             (b'\0' * 5, (0, 0)))
 
     def test_lin2alaw(self):
-        self.assertEqual(audioop.lin2alaw(data[0], 1), '\xd5\xc5\xf5')
-        self.assertEqual(audioop.lin2alaw(data[1], 2), '\xd5\xd5\xd5')
-        self.assertEqual(audioop.lin2alaw(data[2], 4), '\xd5\xd5\xd5')
+        self.assertEqual(audioop.lin2alaw(datas[1], 1),
+                         b'\xd5\x87\xa4\x24\xaa\x2a\x5a')
+        self.assertEqual(audioop.lin2alaw(datas[2], 2),
+                         b'\xd5\x87\xa4\x24\xaa\x2a\x55')
+        self.assertEqual(audioop.lin2alaw(datas[4], 4),
+                         b'\xd5\x87\xa4\x24\xaa\x2a\x55')
 
     def test_alaw2lin(self):
-        # Cursory
-        d = audioop.lin2alaw(data[0], 1)
-        self.assertEqual(audioop.alaw2lin(d, 1), data[0])
-        if endian == 'big':
-            self.assertEqual(audioop.alaw2lin(d, 2),
-                             b'\x00\x08\x01\x08\x02\x10')
-            self.assertEqual(audioop.alaw2lin(d, 4),
-                             b'\x00\x08\x00\x00\x01\x08\x00\x00\x02\x10\x00\x00')
-        else:
-            self.assertEqual(audioop.alaw2lin(d, 2),
-                             b'\x08\x00\x08\x01\x10\x02')
-            self.assertEqual(audioop.alaw2lin(d, 4),
-                             b'\x00\x00\x08\x00\x00\x00\x08\x01\x00\x00\x10\x02')
+        encoded = b'\x00\x03\x24\x2a\x51\x54\x55\x58\x6b\x71\x7f'\
+                  b'\x80\x83\xa4\xaa\xd1\xd4\xd5\xd8\xeb\xf1\xff'
+        src = [-688, -720, -2240, -4032, -9, -3, -1, -27, -244, -82, -106,
+               688, 720, 2240, 4032, 9, 3, 1, 27, 244, 82, 106]
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.alaw2lin(encoded, w),
+                             packs[w](*(x << (w * 8) >> 13 for x in src)))
+
+        encoded = ''.join(chr(x) for x in xrange(256))
+        for w in 2, 4:
+            decoded = audioop.alaw2lin(encoded, w)
+            self.assertEqual(audioop.lin2alaw(decoded, w), encoded)
 
     def test_lin2ulaw(self):
-        self.assertEqual(audioop.lin2ulaw(data[0], 1), '\xff\xe7\xdb')
-        self.assertEqual(audioop.lin2ulaw(data[1], 2), '\xff\xff\xff')
-        self.assertEqual(audioop.lin2ulaw(data[2], 4), '\xff\xff\xff')
+        self.assertEqual(audioop.lin2ulaw(datas[1], 1),
+                         b'\xff\xad\x8e\x0e\x80\x00\x67')
+        self.assertEqual(audioop.lin2ulaw(datas[2], 2),
+                         b'\xff\xad\x8e\x0e\x80\x00\x7e')
+        self.assertEqual(audioop.lin2ulaw(datas[4], 4),
+                         b'\xff\xad\x8e\x0e\x80\x00\x7e')
 
     def test_ulaw2lin(self):
-        # Cursory
-        d = audioop.lin2ulaw(data[0], 1)
-        self.assertEqual(audioop.ulaw2lin(d, 1), data[0])
-        if endian == 'big':
-            self.assertEqual(audioop.ulaw2lin(d, 2),
-                             b'\x00\x00\x01\x04\x02\x0c')
-            self.assertEqual(audioop.ulaw2lin(d, 4),
-                             b'\x00\x00\x00\x00\x01\x04\x00\x00\x02\x0c\x00\x00')
-        else:
-            self.assertEqual(audioop.ulaw2lin(d, 2),
-                             b'\x00\x00\x04\x01\x0c\x02')
-            self.assertEqual(audioop.ulaw2lin(d, 4),
-                             b'\x00\x00\x00\x00\x00\x00\x04\x01\x00\x00\x0c\x02')
+        encoded = b'\x00\x0e\x28\x3f\x57\x6a\x76\x7c\x7e\x7f'\
+                  b'\x80\x8e\xa8\xbf\xd7\xea\xf6\xfc\xfe\xff'
+        src = [-8031, -4447, -1471, -495, -163, -53, -18, -6, -2, 0,
+               8031, 4447, 1471, 495, 163, 53, 18, 6, 2, 0]
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.ulaw2lin(encoded, w),
+                             packs[w](*(x << (w * 8) >> 14 for x in src)))
+
+        # Current u-law implementation has two codes fo 0: 0x7f and 0xff.
+        encoded = ''.join(chr(x) for x in range(127) + range(128, 256))
+        for w in 2, 4:
+            decoded = audioop.ulaw2lin(encoded, w)
+            self.assertEqual(audioop.lin2ulaw(decoded, w), encoded)
 
     def test_mul(self):
-        data2 = []
-        for d in data:
-            str = ''
-            for s in d:
-                str = str + chr(ord(s)*2)
-            data2.append(str)
-        self.assertEqual(audioop.mul(data[0], 1, 2), data2[0])
-        self.assertEqual(audioop.mul(data[1],2, 2), data2[1])
-        self.assertEqual(audioop.mul(data[2], 4, 2), data2[2])
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.mul(b'', w, 2), b'')
+            self.assertEqual(audioop.mul(datas[w], w, 0),
+                             b'\0' * len(datas[w]))
+            self.assertEqual(audioop.mul(datas[w], w, 1),
+                             datas[w])
+        self.assertEqual(audioop.mul(datas[1], 1, 2),
+                         b'\x00\x24\x7f\x80\x7f\x80\xfe')
+        self.assertEqual(audioop.mul(datas[2], 2, 2),
+                packs[2](0, 0x2468, 0x7fff, -0x8000, 0x7fff, -0x8000, -2))
+        self.assertEqual(audioop.mul(datas[4], 4, 2),
+                packs[4](0, 0x2468acf0, 0x7fffffff, -0x80000000,
+                         0x7fffffff, -0x80000000, -2))
 
     def test_ratecv(self):
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.ratecv(b'', w, 1, 8000, 8000, None),
+                             (b'', (-1, ((0, 0),))))
+            self.assertEqual(audioop.ratecv(b'', w, 5, 8000, 8000, None),
+                             (b'', (-1, ((0, 0),) * 5)))
+            self.assertEqual(audioop.ratecv(b'', w, 1, 8000, 16000, None),
+                             (b'', (-2, ((0, 0),))))
+            self.assertEqual(audioop.ratecv(datas[w], w, 1, 8000, 8000, None)[0],
+                             datas[w])
         state = None
-        d1, state = audioop.ratecv(data[0], 1, 1, 8000, 16000, state)
-        d2, state = audioop.ratecv(data[0], 1, 1, 8000, 16000, state)
-        self.assertEqual(d1 + d2, '\000\000\001\001\002\001\000\000\001\001\002')
+        d1, state = audioop.ratecv(b'\x00\x01\x02', 1, 1, 8000, 16000, state)
+        d2, state = audioop.ratecv(b'\x00\x01\x02', 1, 1, 8000, 16000, state)
+        self.assertEqual(d1 + d2, b'\000\000\001\001\002\001\000\000\001\001\002')
+
+        for w in 1, 2, 4:
+            d0, state0 = audioop.ratecv(datas[w], w, 1, 8000, 16000, None)
+            d, state = b'', None
+            for i in range(0, len(datas[w]), w):
+                d1, state = audioop.ratecv(datas[w][i:i + w], w, 1,
+                                           8000, 16000, state)
+                d += d1
+            self.assertEqual(d, d0)
+            self.assertEqual(state, state0)
 
     def test_reverse(self):
-        self.assertEqual(audioop.reverse(data[0], 1), '\2\1\0')
+        for w in 1, 2, 4:
+            self.assertEqual(audioop.reverse(b'', w), b'')
+            self.assertEqual(audioop.reverse(packs[w](0, 1, 2), w),
+                             packs[w](2, 1, 0))
 
     def test_tomono(self):
-        data2 = ''
-        for d in data[0]:
-            data2 = data2 + d + d
-        self.assertEqual(audioop.tomono(data2, 1, 0.5, 0.5), data[0])
+        for w in 1, 2, 4:
+            data1 = datas[w]
+            data2 = bytearray(2 * len(data1))
+            for k in range(w):
+                data2[k::2*w] = data1[k::w]
+            self.assertEqual(audioop.tomono(str(data2), w, 1, 0), data1)
+            self.assertEqual(audioop.tomono(str(data2), w, 0, 1), b'\0' * len(data1))
+            for k in range(w):
+                data2[k+w::2*w] = data1[k::w]
+            self.assertEqual(audioop.tomono(str(data2), w, 0.5, 0.5), data1)
 
     def test_tostereo(self):
-        data2 = ''
-        for d in data[0]:
-            data2 = data2 + d + d
-        self.assertEqual(audioop.tostereo(data[0], 1, 1, 1), data2)
+        for w in 1, 2, 4:
+            data1 = datas[w]
+            data2 = bytearray(2 * len(data1))
+            for k in range(w):
+                data2[k::2*w] = data1[k::w]
+            self.assertEqual(audioop.tostereo(data1, w, 1, 0), data2)
+            self.assertEqual(audioop.tostereo(data1, w, 0, 0), b'\0' * len(data2))
+            for k in range(w):
+                data2[k+w::2*w] = data1[k::w]
+            self.assertEqual(audioop.tostereo(data1, w, 1, 1), data2)
 
     def test_findfactor(self):
-        self.assertEqual(audioop.findfactor(data[1], data[1]), 1.0)
+        self.assertEqual(audioop.findfactor(datas[2], datas[2]), 1.0)
+        self.assertEqual(audioop.findfactor(b'\0' * len(datas[2]), datas[2]),
+                         0.0)
 
     def test_findfit(self):
-        self.assertEqual(audioop.findfit(data[1], data[1]), (0, 1.0))
+        self.assertEqual(audioop.findfit(datas[2], datas[2]), (0, 1.0))
+        self.assertEqual(audioop.findfit(datas[2], packs[2](1, 2, 0)),
+                         (1, 8038.8))
+        self.assertEqual(audioop.findfit(datas[2][:-2] * 5 + datas[2], datas[2]),
+                         (30, 1.0))
 
     def test_findmax(self):
-        self.assertEqual(audioop.findmax(data[1], 1), 2)
+        self.assertEqual(audioop.findmax(datas[2], 1), 5)
 
     def test_getsample(self):
-        for i in range(3):
-            self.assertEqual(audioop.getsample(data[0], 1, i), i)
-            self.assertEqual(audioop.getsample(data[1], 2, i), i)
-            self.assertEqual(audioop.getsample(data[2], 4, i), i)
+        for w in 1, 2, 4:
+            data = packs[w](0, 1, -1, maxvalues[w], minvalues[w])
+            self.assertEqual(audioop.getsample(data, w, 0), 0)
+            self.assertEqual(audioop.getsample(data, w, 1), 1)
+            self.assertEqual(audioop.getsample(data, w, 2), -1)
+            self.assertEqual(audioop.getsample(data, w, 3), maxvalues[w])
+            self.assertEqual(audioop.getsample(data, w, 4), minvalues[w])
 
     def test_negativelen(self):
         # from issue 3306, previously it segfaulted
@@ -220,9 +379,9 @@
             self.assertRaises(audioop.error, audioop.lin2adpcm, data, size, state)
 
     def test_wrongsize(self):
-        data = b'abc'
+        data = b'abcdefgh'
         state = None
-        for size in (-1, 3, 5):
+        for size in (-1, 0, 3, 5, 1024):
             self.assertRaises(audioop.error, audioop.ulaw2lin, data, size)
             self.assertRaises(audioop.error, audioop.alaw2lin, data, size)
             self.assertRaises(audioop.error, audioop.adpcm2lin, data, size, state)
diff --git a/lib-python/2.7/test/test_bigmem.py b/lib-python/2.7/test/test_bigmem.py
--- a/lib-python/2.7/test/test_bigmem.py
+++ b/lib-python/2.7/test/test_bigmem.py
@@ -118,12 +118,13 @@
         except MemoryError:
             pass # acceptable on 32-bit
 
-    @precisionbigmemtest(size=_2G-1, memuse=2)
+    @precisionbigmemtest(size=_2G-1, memuse=4)
     def test_decodeascii(self, size):
         return self.basic_encode_test(size, 'ascii', c='A')
 
     @precisionbigmemtest(size=_4G // 5, memuse=6+2)
     def test_unicode_repr_oflw(self, size):
+        self.skipTest("test crashes - see issue #14904")
         try:
             s = u"\uAAAA"*size
             r = repr(s)
@@ -485,7 +486,7 @@
         self.assertEqual(s.count('.'), 3)
         self.assertEqual(s.count('-'), size * 2)
 
-    @bigmemtest(minsize=_2G + 10, memuse=2)
+    @bigmemtest(minsize=_2G + 10, memuse=5)
     def test_repr_small(self, size):
         s = '-' * size
         s = repr(s)
@@ -497,7 +498,6 @@
         # repr() will create a string four times as large as this 'binary
         # string', but we don't want to allocate much more than twice
         # size in total.  (We do extra testing in test_repr_large())
-        size = size // 5 * 2
         s = '\x00' * size
         s = repr(s)
         self.assertEqual(len(s), size * 4 + 2)
@@ -541,7 +541,7 @@
         self.assertEqual(len(s), size * 2)
         self.assertEqual(s.count('.'), size * 2)
 
-    @bigmemtest(minsize=_2G + 20, memuse=1)
+    @bigmemtest(minsize=_2G + 20, memuse=2)
     def test_slice_and_getitem(self, size):
         SUBSTR = '0123456789'
         sublen = len(SUBSTR)
diff --git a/lib-python/2.7/test/test_bisect.py b/lib-python/2.7/test/test_bisect.py
--- a/lib-python/2.7/test/test_bisect.py
+++ b/lib-python/2.7/test/test_bisect.py
@@ -23,6 +23,28 @@
 import bisect as c_bisect
 
 
+class Range(object):
+    """A trivial xrange()-like object without any integer width limitations."""
+    def __init__(self, start, stop):
+        self.start = start
+        self.stop = stop
+        self.last_insert = None
+
+    def __len__(self):
+        return self.stop - self.start
+
+    def __getitem__(self, idx):
+        n = self.stop - self.start
+        if idx < 0:
+            idx += n
+        if idx >= n:
+            raise IndexError(idx)
+        return self.start + idx
+
+    def insert(self, idx, item):
+        self.last_insert = idx, item
+
+
 class TestBisect(unittest.TestCase):
     module = None
 
@@ -122,6 +144,35 @@
         self.assertRaises(ValueError, mod.insort_left, [1, 2, 3], 5, -1, 3),
         self.assertRaises(ValueError, mod.insort_right, [1, 2, 3], 5, -1, 3),
 
+    def test_large_range(self):
+        # Issue 13496
+        mod = self.module
+        n = sys.maxsize
+        try:
+            data = xrange(n-1)
+        except OverflowError:
+            self.skipTest("can't create a xrange() object of size `sys.maxsize`")
+        self.assertEqual(mod.bisect_left(data, n-3), n-3)
+        self.assertEqual(mod.bisect_right(data, n-3), n-2)
+        self.assertEqual(mod.bisect_left(data, n-3, n-10, n), n-3)
+        self.assertEqual(mod.bisect_right(data, n-3, n-10, n), n-2)
+
+    def test_large_pyrange(self):
+        # Same as above, but without C-imposed limits on range() parameters
+        mod = self.module
+        n = sys.maxsize
+        data = Range(0, n-1)
+        self.assertEqual(mod.bisect_left(data, n-3), n-3)
+        self.assertEqual(mod.bisect_right(data, n-3), n-2)
+        self.assertEqual(mod.bisect_left(data, n-3, n-10, n), n-3)
+        self.assertEqual(mod.bisect_right(data, n-3, n-10, n), n-2)
+        x = n - 100
+        mod.insort_left(data, x, x - 50, x + 50)
+        self.assertEqual(data.last_insert, (x, x))
+        x = n - 200
+        mod.insort_right(data, x, x - 50, x + 50)
+        self.assertEqual(data.last_insert, (x + 1, x))
+
     def test_random(self, n=25):
         from random import randrange
         for i in xrange(n):
@@ -191,7 +242,7 @@
                 else:
                     f = self.module.insort_right
                 f(insorted, digit)
-        self.assertEqual(sorted(insorted), insorted)
+            self.assertEqual(sorted(insorted), insorted)
 
     def test_backcompatibility(self):
         self.assertEqual(self.module.insort, self.module.insort_right)
diff --git a/lib-python/2.7/test/test_builtin.py b/lib-python/2.7/test/test_builtin.py
--- a/lib-python/2.7/test/test_builtin.py
+++ b/lib-python/2.7/test/test_builtin.py
@@ -110,6 +110,7 @@
         self.assertRaises(TypeError, all)                   # No args
         self.assertRaises(TypeError, all, [2, 4, 6], [])    # Too many args
         self.assertEqual(all([]), True)                     # Empty iterator
+        self.assertEqual(all([0, TestFailingBool()]), False)# Short-circuit
         S = [50, 60]
         self.assertEqual(all(x > 42 for x in S), True)
         S = [50, 40, 60]
@@ -119,11 +120,12 @@
         self.assertEqual(any([None, None, None]), False)
         self.assertEqual(any([None, 4, None]), True)
         self.assertRaises(RuntimeError, any, [None, TestFailingBool(), 6])
-        self.assertRaises(RuntimeError, all, TestFailingIter())
+        self.assertRaises(RuntimeError, any, TestFailingIter())
         self.assertRaises(TypeError, any, 10)               # Non-iterable
         self.assertRaises(TypeError, any)                   # No args
         self.assertRaises(TypeError, any, [2, 4, 6], [])    # Too many args
         self.assertEqual(any([]), False)                    # Empty iterator
+        self.assertEqual(any([1, TestFailingBool()]), True) # Short-circuit
         S = [40, 60, 30]
         self.assertEqual(any(x > 42 for x in S), True)
         S = [10, 20, 30]
@@ -680,6 +682,8 @@
 
     # Test input() later, together with raw_input
 
+    # test_int(): see test_int.py for int() tests.
+
     def test_intern(self):
         self.assertRaises(TypeError, intern)
         # This fails if the test is run twice with a constant string,
diff --git a/lib-python/2.7/test/test_bytes.py b/lib-python/2.7/test/test_bytes.py
--- a/lib-python/2.7/test/test_bytes.py
+++ b/lib-python/2.7/test/test_bytes.py
@@ -635,6 +635,26 @@
         b[3:0] = [42, 42, 42]
         self.assertEqual(b, bytearray([0, 1, 2, 42, 42, 42, 3, 4, 5, 6, 7, 8, 9]))
 
+        b[3:] = b'foo'
+        self.assertEqual(b, bytearray([0, 1, 2, 102, 111, 111]))
+
+        b[:3] = memoryview(b'foo')
+        self.assertEqual(b, bytearray([102, 111, 111, 102, 111, 111]))
+
+        b[3:4] = []
+        self.assertEqual(b, bytearray([102, 111, 111, 111, 111]))
+
+        b[1:] = list(b'uuuu')  # this works only on Python2
+        self.assertEqual(b, bytearray([102, 117, 117, 117, 117]))
+
+        for elem in [5, -5, 0, long(10e20), u'str', 2.3, [u'a', u'b'], [[]]]:
+            with self.assertRaises(TypeError):
+                b[3:4] = elem
+
+        for elem in [[254, 255, 256], [-256, 9000]]:
+            with self.assertRaises(ValueError):
+                b[3:4] = elem
+
     def test_extended_set_del_slice(self):
         indices = (0, None, 1, 3, 19, 300, 1<<333, -1, -2, -31, -300)
         for start in indices:
@@ -905,6 +925,7 @@
         self.assertEqual(bytes(b"abc") < b"ab", False)
         self.assertEqual(bytes(b"abc") <= b"ab", False)
 
+    @test.test_support.requires_docstrings
     def test_doc(self):
         self.assertIsNotNone(bytearray.__doc__)
         self.assertTrue(bytearray.__doc__.startswith("bytearray("), bytearray.__doc__)
diff --git a/lib-python/2.7/test/test_bz2.py b/lib-python/2.7/test/test_bz2.py
--- a/lib-python/2.7/test/test_bz2.py
+++ b/lib-python/2.7/test/test_bz2.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 from test import test_support
-from test.test_support import TESTFN, import_module
+from test.test_support import TESTFN, _4G, bigmemtest, import_module, findfile
 
 import unittest
 from cStringIO import StringIO
@@ -23,6 +23,10 @@
     TEXT = 'root:x:0:0:root:/root:/bin/bash\nbin:x:1:1:bin:/bin:\ndaemon:x:2:2:daemon:/sbin:\nadm:x:3:4:adm:/var/adm:\nlp:x:4:7:lp:/var/spool/lpd:\nsync:x:5:0:sync:/sbin:/bin/sync\nshutdown:x:6:0:shutdown:/sbin:/sbin/shutdown\nhalt:x:7:0:halt:/sbin:/sbin/halt\nmail:x:8:12:mail:/var/spool/mail:\nnews:x:9:13:news:/var/spool/news:\nuucp:x:10:14:uucp:/var/spool/uucp:\noperator:x:11:0:operator:/root:\ngames:x:12:100:games:/usr/games:\ngopher:x:13:30:gopher:/usr/lib/gopher-data:\nftp:x:14:50:FTP User:/var/ftp:/bin/bash\nnobody:x:65534:65534:Nobody:/home:\npostfix:x:100:101:postfix:/var/spool/postfix:\nniemeyer:x:500:500::/home/niemeyer:/bin/bash\npostgres:x:101:102:PostgreSQL Server:/var/lib/pgsql:/bin/bash\nmysql:x:102:103:MySQL server:/var/lib/mysql:/bin/bash\nwww:x:103:104::/var/www:/bin/false\n'
     DATA = 'BZh91AY&SY.\xc8N\x18\x00\x01>_\x80\x00\x10@\x02\xff\xf0\x01\x07n\x00?\xe7\xff\xe00\x01\x99\xaa\x00\xc0\x03F\x86\x8c#&\x83F\x9a\x03\x06\xa6\xd0\xa6\x93M\x0fQ\xa7\xa8\x06\x804hh\x12$\x11\xa4i4\xf14S\xd2<Q\xb5\x0fH\xd3\xd4\xdd\xd5\x87\xbb\xf8\x94\r\x8f\xafI\x12\xe1\xc9\xf8/E\x00pu\x89\x12]\xc9\xbbDL\nQ\x0e\t1\x12\xdf\xa0\xc0\x97\xac2O9\x89\x13\x94\x0e\x1c7\x0ed\x95I\x0c\xaaJ\xa4\x18L\x10\x05#\x9c\xaf\xba\xbc/\x97\x8a#C\xc8\xe1\x8cW\xf9\xe2\xd0\xd6M\xa7\x8bXa<e\x84t\xcbL\xb3\xa7\xd9\xcd\xd1\xcb\x84.\xaf\xb3\xab\xab\xad`n}\xa0lh\tE,\x8eZ\x15\x17VH>\x88\xe5\xcd9gd6\x0b\n\xe9\x9b\xd5\x8a\x99\xf7\x08.K\x8ev\xfb\xf7xw\xbb\xdf\xa1\x92\xf1\xdd|/";\xa2\xba\x9f\xd5\xb1#A\xb6\xf6\xb3o\xc9\xc5y\\\xebO\xe7\x85\x9a\xbc\xb6f8\x952\xd5\xd7"%\x89>V,\xf7\xa6z\xe2\x9f\xa3\xdf\x11\x11"\xd6E)I\xa9\x13^\xca\xf3r\xd0\x03U\x922\xf26\xec\xb6\xed\x8b\xc3U\x13\x9d\xc5\x170\xa4\xfa^\x92\xacDF\x8a\x97\xd6\x19\xfe\xdd\xb8\xbd\x1a\x9a\x19\xa3\x80ankR\x8b\xe5\xd83]\xa9\xc6\x08\x82f\xf6\xb9"6l$\xb8j@\xc0\x8a\xb0l1..\xbak\x83ls\x15\xbc\xf4\xc1\x13\xbe\xf8E\xb8\x9d\r\xa8\x9dk\x84\xd3n\xfa\xacQ\x07\xb1%y\xaav\xb4\x08\xe0z\x1b\x16\xf5\x04\xe9\xcc\xb9\x08z\x1en7.G\xfc]\xc9\x14\xe1B@\xbb!8`'
     DATA_CRLF = 'BZh91AY&SY\xaez\xbbN\x00\x01H\xdf\x80\x00\x12@\x02\xff\xf0\x01\x07n\x00?\xe7\xff\xe0@\x01\xbc\xc6`\x86*\x8d=M\xa9\x9a\x86\xd0L@\x0fI\xa6!\xa1\x13\xc8\x88jdi\x8d@\x03@\x1a\x1a\x0c\x0c\x83 \x00\xc4h2\x19\x01\x82D\x84e\t\xe8\x99\x89\x19\x1ah\x00\r\x1a\x11\xaf\x9b\x0fG\xf5(\x1b\x1f?\t\x12\xcf\xb5\xfc\x95E\x00ps\x89\x12^\xa4\xdd\xa2&\x05(\x87\x04\x98\x89u\xe40%\xb6\x19\'\x8c\xc4\x89\xca\x07\x0e\x1b!\x91UIFU%C\x994!DI\xd2\xfa\xf0\xf1N8W\xde\x13A\xf5\x9cr%?\x9f3;I45A\xd1\x8bT\xb1<l\xba\xcb_\xc00xY\x17r\x17\x88\x08\x08@\xa0\ry@\x10\x04$)`\xf2\xce\x89z\xb0s\xec\x9b.iW\x9d\x81\xb5-+t\x9f\x1a\'\x97dB\xf5x\xb5\xbe.[.\xd7\x0e\x81\xe7\x08\x1cN`\x88\x10\xca\x87\xc3!"\x80\x92R\xa1/\xd1\xc0\xe6mf\xac\xbd\x99\xcca\xb3\x8780>\xa4\xc7\x8d\x1a\\"\xad\xa1\xabyBg\x15\xb9l\x88\x88\x91k"\x94\xa4\xd4\x89\xae*\xa6\x0b\x10\x0c\xd6\xd4m\xe86\xec\xb5j\x8a\x86j\';\xca.\x01I\xf2\xaaJ\xe8\x88\x8cU+t3\xfb\x0c\n\xa33\x13r2\r\x16\xe0\xb3(\xbf\x1d\x83r\xe7M\xf0D\x1365\xd8\x88\xd3\xa4\x92\xcb2\x06\x04\\\xc1\xb0\xea//\xbek&\xd8\xe6+t\xe5\xa1\x13\xada\x16\xder5"w]\xa2i\xb7[\x97R \xe2IT\xcd;Z\x04dk4\xad\x8a\t\xd3\x81z\x10\xf1:^`\xab\x1f\xc5\xdc\x91N\x14$+\x9e\xae\xd3\x80'
+    EMPTY_DATA = 'BZh9\x17rE8P\x90\x00\x00\x00\x00'
+
+    with open(findfile("testbz2_bigmem.bz2"), "rb") as f:
+        DATA_BIGMEM = f.read()
 
     if has_cmdline_bunzip2:
         def decompress(self, data):
@@ -43,6 +47,7 @@
         def decompress(self, data):
             return bz2.decompress(data)
 
+
 class BZ2FileTest(BaseTest):
     "Test BZ2File type miscellaneous methods."
 
@@ -323,6 +328,24 @@
             self.assertRaises(ValueError, f.readline)
             self.assertRaises(ValueError, f.readlines)
 
+    def test_read_truncated(self):
+        # Drop the eos_magic field (6 bytes) and CRC (4 bytes).
+        truncated = self.DATA[:-10]
+        with open(self.filename, 'wb') as f:
+            f.write(truncated)
+        with BZ2File(self.filename) as f:
+            self.assertRaises(EOFError, f.read)
+        with BZ2File(self.filename) as f:
+            self.assertEqual(f.read(len(self.TEXT)), self.TEXT)
+            self.assertRaises(EOFError, f.read, 1)
+        # Incomplete 4-byte file header, and block header of at least 146 bits.
+        for i in range(22):
+            with open(self.filename, 'wb') as f:
+                f.write(truncated[:i])
+            with BZ2File(self.filename) as f:
+                self.assertRaises(EOFError, f.read, 1)
+
+
 class BZ2CompressorTest(BaseTest):
     def testCompress(self):
         # "Test BZ2Compressor.compress()/flush()"
@@ -332,6 +355,13 @@
         data += bz2c.flush()
         self.assertEqual(self.decompress(data), self.TEXT)
 
+    def testCompressEmptyString(self):
+        # "Test BZ2Compressor.compress()/flush() of empty string"
+        bz2c = BZ2Compressor()
+        data = bz2c.compress('')
+        data += bz2c.flush()
+        self.assertEqual(data, self.EMPTY_DATA)
+
     def testCompressChunks10(self):
         # "Test BZ2Compressor.compress()/flush() with chunks of 10 bytes"
         bz2c = BZ2Compressor()
@@ -346,6 +376,17 @@
         data += bz2c.flush()
         self.assertEqual(self.decompress(data), self.TEXT)
 
+    @bigmemtest(_4G, memuse=1.25)
+    def testBigmem(self, size):
+        text = "a" * size
+        bz2c = bz2.BZ2Compressor()
+        data = bz2c.compress(text) + bz2c.flush()
+        del text
+        text = self.decompress(data)
+        self.assertEqual(len(text), size)
+        self.assertEqual(text.strip("a"), "")
+
+
 class BZ2DecompressorTest(BaseTest):
     def test_Constructor(self):
         self.assertRaises(TypeError, BZ2Decompressor, 42)
@@ -383,6 +424,16 @@
         bz2d = BZ2Decompressor()
         text = bz2d.decompress(self.DATA)
         self.assertRaises(EOFError, bz2d.decompress, "anything")
+        self.assertRaises(EOFError, bz2d.decompress, "")
+
+    @bigmemtest(_4G, memuse=1.25)
+    def testBigmem(self, size):
+        # Issue #14398: decompression fails when output data is >=2GB.
+        if size < _4G:
+            self.skipTest("Test needs 5GB of memory to run.")
+        text = bz2.BZ2Decompressor().decompress(self.DATA_BIGMEM)
+        self.assertEqual(len(text), _4G)
+        self.assertEqual(text.strip("\0"), "")
 
 
 class FuncTest(BaseTest):
@@ -393,6 +444,11 @@
         data = bz2.compress(self.TEXT)
         self.assertEqual(self.decompress(data), self.TEXT)
 
+    def testCompressEmptyString(self):
+        # "Test compress() of empty string"
+        text = bz2.compress('')
+        self.assertEqual(text, self.EMPTY_DATA)
+
     def testDecompress(self):
         # "Test decompress() function"
         text = bz2.decompress(self.DATA)
@@ -403,10 +459,33 @@
         text = bz2.decompress("")
         self.assertEqual(text, "")
 
+    def testDecompressToEmptyString(self):
+        # "Test decompress() of minimal bz2 data to empty string"
+        text = bz2.decompress(self.EMPTY_DATA)
+        self.assertEqual(text, '')
+
     def testDecompressIncomplete(self):
         # "Test decompress() function with incomplete data"
         self.assertRaises(ValueError, bz2.decompress, self.DATA[:-10])
 
+    @bigmemtest(_4G, memuse=1.25)
+    def testCompressBigmem(self, size):
+        text = "a" * size
+        data = bz2.compress(text)
+        del text
+        text = self.decompress(data)
+        self.assertEqual(len(text), size)
+        self.assertEqual(text.strip("a"), "")
+
+    @bigmemtest(_4G, memuse=1.25)
+    def testDecompressBigmem(self, size):
+        # Issue #14398: decompression fails when output data is >=2GB.
+        if size < _4G:
+            self.skipTest("Test needs 5GB of memory to run.")
+        text = bz2.decompress(self.DATA_BIGMEM)
+        self.assertEqual(len(text), _4G)
+        self.assertEqual(text.strip("\0"), "")
+
 def test_main():
     test_support.run_unittest(
         BZ2FileTest,
diff --git a/lib-python/2.7/test/test_calendar.py b/lib-python/2.7/test/test_calendar.py
--- a/lib-python/2.7/test/test_calendar.py
+++ b/lib-python/2.7/test/test_calendar.py
@@ -3,6 +3,7 @@
 
 from test import test_support
 import locale
+import datetime
 
 
 result_2004_text = """
@@ -254,13 +255,30 @@
         # (it is still not thread-safe though)
         old_october = calendar.TextCalendar().formatmonthname(2010, 10, 10)
         try:
-            calendar.LocaleTextCalendar(locale='').formatmonthname(2010, 10, 10)
+            cal = calendar.LocaleTextCalendar(locale='')
+            local_weekday = cal.formatweekday(1, 10)
+            local_month = cal.formatmonthname(2010, 10, 10)
         except locale.Error:
             # cannot set the system default locale -- skip rest of test
-            return
-        calendar.LocaleHTMLCalendar(locale='').formatmonthname(2010, 10)
+            raise unittest.SkipTest('cannot set the system default locale')
+        # should be encodable
+        local_weekday.encode('utf-8')
+        local_month.encode('utf-8')
+        self.assertEqual(len(local_weekday), 10)
+        self.assertGreaterEqual(len(local_month), 10)
+        cal = calendar.LocaleHTMLCalendar(locale='')
+        local_weekday = cal.formatweekday(1)
+        local_month = cal.formatmonthname(2010, 10)
+        # should be encodable
+        local_weekday.encode('utf-8')
+        local_month.encode('utf-8')
         new_october = calendar.TextCalendar().formatmonthname(2010, 10, 10)
-        self.assertEquals(old_october, new_october)
+        self.assertEqual(old_october, new_october)
+
+    def test_itermonthdates(self):
+        # ensure itermonthdates doesn't overflow after datetime.MAXYEAR
+        # see #15421
+        list(calendar.Calendar().itermonthdates(datetime.MAXYEAR, 12))
 
 
 class MonthCalendarTestCase(unittest.TestCase):
diff --git a/lib-python/2.7/test/test_capi.py b/lib-python/2.7/test/test_capi.py
--- a/lib-python/2.7/test/test_capi.py
+++ b/lib-python/2.7/test/test_capi.py
@@ -8,8 +8,10 @@
 import unittest
 from test import test_support
 try:
+    import thread
     import threading
 except ImportError:
+    thread = None
     threading = None
 import _testcapi
 
@@ -96,8 +98,32 @@
         self.pendingcalls_wait(l, n)
 
 
+ at unittest.skipUnless(threading and thread, 'Threading required for this test.')
+class TestThreadState(unittest.TestCase):
+
+    @test_support.reap_threads
+    def test_thread_state(self):
+        # some extra thread-state tests driven via _testcapi
+        def target():
+            idents = []
+
+            def callback():
+                idents.append(thread.get_ident())
+
+            _testcapi._test_thread_state(callback)
+            a = b = callback
+            time.sleep(1)
+            # Check our main thread is in the list exactly 3 times.
+            self.assertEqual(idents.count(thread.get_ident()), 3,
+                             "Couldn't find main thread correctly in the list")
+
+        target()
+        t = threading.Thread(target=target)
+        t.start()
+        t.join()
+
+
 def test_main():
-
     for name in dir(_testcapi):
         if name.startswith('test_'):
             test = getattr(_testcapi, name)
@@ -108,33 +134,7 @@
             except _testcapi.error:
                 raise test_support.TestFailed, sys.exc_info()[1]
 
-    # some extra thread-state tests driven via _testcapi
-    def TestThreadState():
-        if test_support.verbose:
-            print "auto-thread-state"
-
-        idents = []
-
-        def callback():
-            idents.append(thread.get_ident())
-
-        _testcapi._test_thread_state(callback)
-        a = b = callback
-        time.sleep(1)
-        # Check our main thread is in the list exactly 3 times.
-        if idents.count(thread.get_ident()) != 3:
-            raise test_support.TestFailed, \
-                  "Couldn't find main thread correctly in the list"
-
-    if threading:
-        import thread
-        import time
-        TestThreadState()
-        t=threading.Thread(target=TestThreadState)
-        t.start()
-        t.join()
-
-    test_support.run_unittest(TestPendingCalls)
+    test_support.run_unittest(TestPendingCalls, TestThreadState)
 
 if __name__ == "__main__":
     test_main()
diff --git a/lib-python/2.7/test/test_cmd.py b/lib-python/2.7/test/test_cmd.py
--- a/lib-python/2.7/test/test_cmd.py
+++ b/lib-python/2.7/test/test_cmd.py
@@ -84,11 +84,11 @@
     <BLANKLINE>
     Documented commands (type help <topic>):
     ========================================
-    add
+    add  help
     <BLANKLINE>
     Undocumented commands:
     ======================
-    exit  help  shell
+    exit  shell
     <BLANKLINE>
 
     Test for the function print_topics():
@@ -125,11 +125,11 @@
     <BLANKLINE>
     Documented commands (type help <topic>):
     ========================================
-    add
+    add  help
     <BLANKLINE>
     Undocumented commands:
     ======================
-    exit  help  shell
+    exit  shell
     <BLANKLINE>
     help text for add
     Hello from postloop
diff --git a/lib-python/2.7/test/test_cmd_line.py b/lib-python/2.7/test/test_cmd_line.py
--- a/lib-python/2.7/test/test_cmd_line.py
+++ b/lib-python/2.7/test/test_cmd_line.py
@@ -2,9 +2,13 @@
 # All tests are executed with environment variables ignored
 # See test_cmd_line_script.py for testing of script execution
 
-import test.test_support, unittest
+import test.test_support
 import sys
-from test.script_helper import spawn_python, kill_python, python_exit_code
+import unittest
+from test.script_helper import (
+    assert_python_ok, assert_python_failure, spawn_python, kill_python,
+    python_exit_code
+)
 
 
 class CmdLineTest(unittest.TestCase):
@@ -101,6 +105,36 @@
         data = self.start_python('-R', '-c', code)
         self.assertTrue('hash_randomization=1' in data)
 
+    def test_del___main__(self):
+        # Issue #15001: PyRun_SimpleFileExFlags() did crash because it kept a
+        # borrowed reference to the dict of __main__ module and later modify
+        # the dict whereas the module was destroyed
+        filename = test.test_support.TESTFN
+        self.addCleanup(test.test_support.unlink, filename)
+        with open(filename, "w") as script:
+            print >>script, "import sys"
+            print >>script, "del sys.modules['__main__']"
+        assert_python_ok(filename)
+
+    def test_unknown_options(self):
+        rc, out, err = assert_python_failure('-E', '-z')
+        self.assertIn(b'Unknown option: -z', err)
+        self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
+        self.assertEqual(b'', out)
+        # Add "without='-E'" to prevent _assert_python to append -E
+        # to env_vars and change the output of stderr
+        rc, out, err = assert_python_failure('-z', without='-E')
+        self.assertIn(b'Unknown option: -z', err)
+        self.assertEqual(err.splitlines().count(b'Unknown option: -z'), 1)
+        self.assertEqual(b'', out)
+        rc, out, err = assert_python_failure('-a', '-z', without='-E')
+        self.assertIn(b'Unknown option: -a', err)
+        # only the first unknown option is reported
+        self.assertNotIn(b'Unknown option: -z', err)
+        self.assertEqual(err.splitlines().count(b'Unknown option: -a'), 1)
+        self.assertEqual(b'', out)
+
+
 def test_main():
     test.test_support.run_unittest(CmdLineTest)
     test.test_support.reap_children()
diff --git a/lib-python/2.7/test/test_cmd_line_script.py b/lib-python/2.7/test/test_cmd_line_script.py
--- a/lib-python/2.7/test/test_cmd_line_script.py
+++ b/lib-python/2.7/test/test_cmd_line_script.py
@@ -6,11 +6,14 @@
 import test.test_support
 from test.script_helper import (run_python,
                                 temp_dir, make_script, compile_script,
-                                make_pkg, make_zip_script, make_zip_pkg)
+                                assert_python_failure, make_pkg,
+                                make_zip_script, make_zip_pkg)
 
 verbose = test.test_support.verbose
 
 
+example_args = ['test1', 'test2', 'test3']
+
 test_source = """\
 # Script may be run with optimisation enabled, so don't rely on assert
 # statements being executed
@@ -204,6 +207,19 @@
             launch_name = _make_launch_script(script_dir, 'launch', 'test_pkg')
             self._check_import_error(launch_name, msg)
 
+    def test_dash_m_error_code_is_one(self):
+        # If a module is invoked with the -m command line flag
+        # and results in an error that the return code to the
+        # shell is '1'
+        with temp_dir() as script_dir:
+            pkg_dir = os.path.join(script_dir, 'test_pkg')
+            make_pkg(pkg_dir)
+            script_name = _make_test_script(pkg_dir, 'other', "if __name__ == '__main__': raise ValueError")
+            rc, out, err = assert_python_failure('-m', 'test_pkg.other', *example_args)
+            if verbose > 1:
+                print(out)
+            self.assertEqual(rc, 1)
+
 
 def test_main():
     test.test_support.run_unittest(CmdLineTest)
diff --git a/lib-python/2.7/test/test_codeccallbacks.py b/lib-python/2.7/test/test_codeccallbacks.py
--- a/lib-python/2.7/test/test_codeccallbacks.py
+++ b/lib-python/2.7/test/test_codeccallbacks.py
@@ -262,12 +262,12 @@
 
         self.assertEqual(
             "\\u3042\u3xxx".decode("unicode-escape", "test.handler1"),
-            u"\u3042[<92><117><51><120>]xx"
+            u"\u3042[<92><117><51>]xxx"
         )
 
         self.assertEqual(
             "\\u3042\u3xx".decode("unicode-escape", "test.handler1"),
-            u"\u3042[<92><117><51><120><120>]"
+            u"\u3042[<92><117><51>]xx"
         )
 
         self.assertEqual(
@@ -717,7 +717,7 @@
                 raise ValueError
         self.assertRaises(UnicodeError, codecs.charmap_decode, "\xff", "strict", {0xff: None})
         self.assertRaises(ValueError, codecs.charmap_decode, "\xff", "strict", D())
-        self.assertRaises(TypeError, codecs.charmap_decode, "\xff", "strict", {0xff: sys.maxunicode+1})
+        self.assertRaises(TypeError, codecs.charmap_decode, "\xff", "strict", {0xff: 0x110000})
 
     def test_encodehelper(self):
         # enhance coverage of:
diff --git a/lib-python/2.7/test/test_codecs.py b/lib-python/2.7/test/test_codecs.py
--- a/lib-python/2.7/test/test_codecs.py
+++ b/lib-python/2.7/test/test_codecs.py
@@ -4,6 +4,11 @@
 import locale
 import sys, StringIO, _testcapi
 
+def coding_checker(self, coder):
+    def check(input, expect):
+        self.assertEqual(coder(input), (expect, len(input)))
+    return check
+
 class Queue(object):
     """
     queue: write bytes at one end, read bytes from the other end
@@ -281,7 +286,7 @@
 
     def test_partial(self):
         self.check_partial(
-            u"\x00\xff\u0100\uffff",
+            u"\x00\xff\u0100\uffff\U00010000",
             [
                 u"", # first byte of BOM read
                 u"", # second byte of BOM read
@@ -303,6 +308,10 @@
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff\U00010000",
             ]
         )
 
@@ -331,7 +340,7 @@
 
     def test_partial(self):
         self.check_partial(
-            u"\x00\xff\u0100\uffff",
+            u"\x00\xff\u0100\uffff\U00010000",
             [
                 u"",
                 u"",
@@ -349,6 +358,10 @@
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff\U00010000",
             ]
         )
 
@@ -371,7 +384,7 @@
 
     def test_partial(self):
         self.check_partial(
-            u"\x00\xff\u0100\uffff",
+            u"\x00\xff\u0100\uffff\U00010000",
             [
                 u"",
                 u"",
@@ -389,6 +402,10 @@
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff\U00010000",
             ]
         )
 
@@ -439,7 +456,7 @@
 
     def test_partial(self):
         self.check_partial(
-            u"\x00\xff\u0100\uffff",
+            u"\x00\xff\u0100\uffff\U00010000",
             [
                 u"", # first byte of BOM read
                 u"", # second byte of BOM read => byteorder known
@@ -451,6 +468,10 @@
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff\U00010000",
             ]
         )
 
@@ -481,7 +502,7 @@
 
     def test_partial(self):
         self.check_partial(
-            u"\x00\xff\u0100\uffff",
+            u"\x00\xff\u0100\uffff\U00010000",
             [
                 u"",
                 u"\x00",
@@ -491,18 +512,34 @@
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff\U00010000",
             ]
         )
 
     def test_errors(self):
-        self.assertRaises(UnicodeDecodeError, codecs.utf_16_le_decode, "\xff", "strict", True)
+        tests = [
+            (b'\xff', u'\ufffd'),
+            (b'A\x00Z', u'A\ufffd'),
+            (b'A\x00B\x00C\x00D\x00Z', u'ABCD\ufffd'),
+            (b'\x00\xd8', u'\ufffd'),
+            (b'\x00\xd8A', u'\ufffd'),
+            (b'\x00\xd8A\x00', u'\ufffdA'),
+            (b'\x00\xdcA\x00', u'\ufffdA'),
+        ]
+        for raw, expected in tests:
+            self.assertRaises(UnicodeDecodeError, codecs.utf_16_le_decode,
+                              raw, 'strict', True)
+            self.assertEqual(raw.decode('utf-16le', 'replace'), expected)
 
 class UTF16BETest(ReadTest):
     encoding = "utf-16-be"
 
     def test_partial(self):
         self.check_partial(
-            u"\x00\xff\u0100\uffff",
+            u"\x00\xff\u0100\uffff\U00010000",
             [
                 u"",
                 u"\x00",
@@ -512,18 +549,34 @@
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100",
                 u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff",
+                u"\x00\xff\u0100\uffff\U00010000",
             ]
         )
 
     def test_errors(self):
-        self.assertRaises(UnicodeDecodeError, codecs.utf_16_be_decode, "\xff", "strict", True)
+        tests = [
+            (b'\xff', u'\ufffd'),
+            (b'\x00A\xff', u'A\ufffd'),
+            (b'\x00A\x00B\x00C\x00DZ', u'ABCD\ufffd'),
+            (b'\xd8\x00', u'\ufffd'),
+            (b'\xd8\x00\xdc', u'\ufffd'),
+            (b'\xd8\x00\x00A', u'\ufffdA'),
+            (b'\xdc\x00\x00A', u'\ufffdA'),
+        ]
+        for raw, expected in tests:
+            self.assertRaises(UnicodeDecodeError, codecs.utf_16_be_decode,
+                              raw, 'strict', True)
+            self.assertEqual(raw.decode('utf-16be', 'replace'), expected)
 
 class UTF8Test(ReadTest):
     encoding = "utf-8"
 
     def test_partial(self):
         self.check_partial(
-            u"\x00\xff\u07ff\u0800\uffff",
+            u"\x00\xff\u07ff\u0800\uffff\U00010000",
             [
                 u"\x00",
                 u"\x00",
@@ -536,6 +589,10 @@
                 u"\x00\xff\u07ff\u0800",
                 u"\x00\xff\u07ff\u0800",
                 u"\x00\xff\u07ff\u0800\uffff",
+                u"\x00\xff\u07ff\u0800\uffff",
+                u"\x00\xff\u07ff\u0800\uffff",
+                u"\x00\xff\u07ff\u0800\uffff",
+                u"\x00\xff\u07ff\u0800\uffff\U00010000",
             ]
         )
 
@@ -595,7 +652,7 @@
 
     def test_partial(self):
         self.check_partial(
-            u"\ufeff\x00\xff\u07ff\u0800\uffff",
+            u"\ufeff\x00\xff\u07ff\u0800\uffff\U00010000",
             [
                 u"",
                 u"",
@@ -614,6 +671,10 @@
                 u"\ufeff\x00\xff\u07ff\u0800",
                 u"\ufeff\x00\xff\u07ff\u0800",
                 u"\ufeff\x00\xff\u07ff\u0800\uffff",
+                u"\ufeff\x00\xff\u07ff\u0800\uffff",
+                u"\ufeff\x00\xff\u07ff\u0800\uffff",
+                u"\ufeff\x00\xff\u07ff\u0800\uffff",
+                u"\ufeff\x00\xff\u07ff\u0800\uffff\U00010000",
             ]
         )
 
@@ -674,6 +735,54 @@
     def test_empty(self):
         self.assertEqual(codecs.escape_decode(""), ("", 0))
 
+    def test_raw(self):
+        decode = codecs.escape_decode
+        for b in range(256):
+            b = chr(b)
+            if b != '\\':
+                self.assertEqual(decode(b + '0'), (b + '0', 2))
+
+    def test_escape(self):
+        decode = codecs.escape_decode
+        check = coding_checker(self, decode)
+        check(b"[\\\n]", b"[]")
+        check(br'[\"]', b'["]')
+        check(br"[\']", b"[']")
+        check(br"[\\]", br"[\]")
+        check(br"[\a]", b"[\x07]")
+        check(br"[\b]", b"[\x08]")
+        check(br"[\t]", b"[\x09]")
+        check(br"[\n]", b"[\x0a]")
+        check(br"[\v]", b"[\x0b]")
+        check(br"[\f]", b"[\x0c]")
+        check(br"[\r]", b"[\x0d]")
+        check(br"[\7]", b"[\x07]")
+        check(br"[\8]", br"[\8]")
+        check(br"[\78]", b"[\x078]")
+        check(br"[\41]", b"[!]")
+        check(br"[\418]", b"[!8]")
+        check(br"[\101]", b"[A]")
+        check(br"[\1010]", b"[A0]")
+        check(br"[\501]", b"[A]")
+        check(br"[\x41]", b"[A]")
+        check(br"[\X41]", br"[\X41]")
+        check(br"[\x410]", b"[A0]")
+        for b in range(256):
+            b = chr(b)
+            if b not in '\n"\'\\abtnvfr01234567x':
+                check('\\' + b, '\\' + b)
+
+    def test_errors(self):
+        decode = codecs.escape_decode
+        self.assertRaises(ValueError, decode, br"\x")
+        self.assertRaises(ValueError, decode, br"[\x]")
+        self.assertEqual(decode(br"[\x]\x", "ignore"), (b"[]", 6))
+        self.assertEqual(decode(br"[\x]\x", "replace"), (b"[?]?", 6))
+        self.assertRaises(ValueError, decode, br"\x0")
+        self.assertRaises(ValueError, decode, br"[\x0]")
+        self.assertEqual(decode(br"[\x0]\x0", "ignore"), (b"[]", 8))
+        self.assertEqual(decode(br"[\x0]\x0", "replace"), (b"[?]?", 8))
+
 class RecodingTest(unittest.TestCase):
     def test_recoding(self):
         f = StringIO.StringIO()
@@ -1495,6 +1604,14 @@
             (u"abc", 3)
         )
 
+        self.assertRaises(UnicodeDecodeError,
+            codecs.charmap_decode, b"\x00\x01\x02", "strict", u"ab"
+        )
+
+        self.assertRaises(UnicodeDecodeError,
+            codecs.charmap_decode, "\x00\x01\x02", "strict", u"ab\ufffe"
+        )
+
         self.assertEqual(
             codecs.charmap_decode("\x00\x01\x02", "replace", u"ab"),
             (u"ab\ufffd", 3)
@@ -1521,6 +1638,149 @@
             (u"", len(allbytes))
         )
 
+    def test_decode_with_int2str_map(self):
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "strict",
+                                  {0: u'a', 1: u'b', 2: u'c'}),
+            (u"abc", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "strict",
+                                  {0: u'Aa', 1: u'Bb', 2: u'Cc'}),
+            (u"AaBbCc", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "strict",
+                                  {0: u'\U0010FFFF', 1: u'b', 2: u'c'}),
+            (u"\U0010FFFFbc", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "strict",
+                                  {0: u'a', 1: u'b', 2: u''}),
+            (u"ab", 3)
+        )
+
+        self.assertRaises(UnicodeDecodeError,
+            codecs.charmap_decode, "\x00\x01\x02", "strict",
+                                   {0: u'a', 1: u'b'}
+        )
+
+        self.assertRaises(UnicodeDecodeError,
+            codecs.charmap_decode, "\x00\x01\x02", "strict",
+                                   {0: u'a', 1: u'b', 2: None}
+        )
+
+        # Issue #14850
+        self.assertRaises(UnicodeDecodeError,
+            codecs.charmap_decode, "\x00\x01\x02", "strict",
+                                   {0: u'a', 1: u'b', 2: u'\ufffe'}
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "replace",
+                                  {0: u'a', 1: u'b'}),
+            (u"ab\ufffd", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "replace",
+                                  {0: u'a', 1: u'b', 2: None}),
+            (u"ab\ufffd", 3)
+        )
+
+        # Issue #14850
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "replace",
+                                  {0: u'a', 1: u'b', 2: u'\ufffe'}),
+            (u"ab\ufffd", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "ignore",
+                                  {0: u'a', 1: u'b'}),
+            (u"ab", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "ignore",
+                                  {0: u'a', 1: u'b', 2: None}),
+            (u"ab", 3)
+        )
+
+        # Issue #14850
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "ignore",
+                                  {0: u'a', 1: u'b', 2: u'\ufffe'}),
+            (u"ab", 3)
+        )
+
+        allbytes = "".join(chr(i) for i in xrange(256))
+        self.assertEqual(
+            codecs.charmap_decode(allbytes, "ignore", {}),
+            (u"", len(allbytes))
+        )
+
+    def test_decode_with_int2int_map(self):
+        a = ord(u'a')
+        b = ord(u'b')
+        c = ord(u'c')
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "strict",
+                                  {0: a, 1: b, 2: c}),
+            (u"abc", 3)
+        )
+
+        # Issue #15379
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "strict",
+                                  {0: 0x10FFFF, 1: b, 2: c}),
+            (u"\U0010FFFFbc", 3)
+        )
+
+        self.assertRaises(TypeError,
+            codecs.charmap_decode, "\x00\x01\x02", "strict",
+                                   {0: 0x110000, 1: b, 2: c}
+        )
+
+        self.assertRaises(UnicodeDecodeError,
+            codecs.charmap_decode, "\x00\x01\x02", "strict",
+                                   {0: a, 1: b},
+        )
+
+        self.assertRaises(UnicodeDecodeError,
+            codecs.charmap_decode, "\x00\x01\x02", "strict",
+                                   {0: a, 1: b, 2: 0xFFFE},
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "replace",
+                                  {0: a, 1: b}),
+            (u"ab\ufffd", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "replace",
+                                  {0: a, 1: b, 2: 0xFFFE}),
+            (u"ab\ufffd", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "ignore",
+                                  {0: a, 1: b}),
+            (u"ab", 3)
+        )
+
+        self.assertEqual(
+            codecs.charmap_decode("\x00\x01\x02", "ignore",
+                                  {0: a, 1: b, 2: 0xFFFE}),
+            (u"ab", 3)
+        )
+
+
 class WithStmtTest(unittest.TestCase):
     def test_encodedfile(self):
         f = StringIO.StringIO("\xc3\xbc")
@@ -1535,6 +1795,134 @@
             self.assertEqual(srw.read(), u"\xfc")
 
 
+class UnicodeEscapeTest(unittest.TestCase):
+    def test_empty(self):
+        self.assertEqual(codecs.unicode_escape_encode(u""), ("", 0))
+        self.assertEqual(codecs.unicode_escape_decode(""), (u"", 0))
+
+    def test_raw_encode(self):
+        encode = codecs.unicode_escape_encode
+        for b in range(32, 127):
+            if b != ord('\\'):
+                self.assertEqual(encode(unichr(b)), (chr(b), 1))
+
+    def test_raw_decode(self):
+        decode = codecs.unicode_escape_decode
+        for b in range(256):
+            if b != ord('\\'):
+                self.assertEqual(decode(chr(b) + '0'), (unichr(b) + u'0', 2))
+
+    def test_escape_encode(self):
+        encode = codecs.unicode_escape_encode
+        check = coding_checker(self, encode)
+        check(u'\t', r'\t')
+        check(u'\n', r'\n')
+        check(u'\r', r'\r')
+        check(u'\\', r'\\')
+        for b in range(32):
+            if chr(b) not in '\t\n\r':
+                check(unichr(b), '\\x%02x' % b)
+        for b in range(127, 256):
+            check(unichr(b), '\\x%02x' % b)
+        check(u'\u20ac', r'\u20ac')
+        check(u'\U0001d120', r'\U0001d120')
+
+    def test_escape_decode(self):
+        decode = codecs.unicode_escape_decode
+        check = coding_checker(self, decode)
+        check("[\\\n]", u"[]")
+        check(r'[\"]', u'["]')
+        check(r"[\']", u"[']")
+        check(r"[\\]", ur"[\]")
+        check(r"[\a]", u"[\x07]")
+        check(r"[\b]", u"[\x08]")
+        check(r"[\t]", u"[\x09]")
+        check(r"[\n]", u"[\x0a]")
+        check(r"[\v]", u"[\x0b]")
+        check(r"[\f]", u"[\x0c]")
+        check(r"[\r]", u"[\x0d]")
+        check(r"[\7]", u"[\x07]")
+        check(r"[\8]", ur"[\8]")
+        check(r"[\78]", u"[\x078]")
+        check(r"[\41]", u"[!]")
+        check(r"[\418]", u"[!8]")
+        check(r"[\101]", u"[A]")
+        check(r"[\1010]", u"[A0]")
+        check(r"[\x41]", u"[A]")
+        check(r"[\x410]", u"[A0]")
+        check(r"\u20ac", u"\u20ac")
+        check(r"\U0001d120", u"\U0001d120")
+        for b in range(256):
+            if chr(b) not in '\n"\'\\abtnvfr01234567xuUN':
+                check('\\' + chr(b), u'\\' + unichr(b))
+
+    def test_decode_errors(self):
+        decode = codecs.unicode_escape_decode
+        for c, d in ('x', 2), ('u', 4), ('U', 4):
+            for i in range(d):
+                self.assertRaises(UnicodeDecodeError, decode,
+                                  "\\" + c + "0"*i)
+                self.assertRaises(UnicodeDecodeError, decode,
+                                  "[\\" + c + "0"*i + "]")
+                data = "[\\" + c + "0"*i + "]\\" + c + "0"*i
+                self.assertEqual(decode(data, "ignore"), (u"[]", len(data)))
+                self.assertEqual(decode(data, "replace"),
+                                 (u"[\ufffd]\ufffd", len(data)))
+        self.assertRaises(UnicodeDecodeError, decode, r"\U00110000")
+        self.assertEqual(decode(r"\U00110000", "ignore"), (u"", 10))
+        self.assertEqual(decode(r"\U00110000", "replace"), (u"\ufffd", 10))
+
+
+class RawUnicodeEscapeTest(unittest.TestCase):
+    def test_empty(self):
+        self.assertEqual(codecs.raw_unicode_escape_encode(u""), ("", 0))
+        self.assertEqual(codecs.raw_unicode_escape_decode(""), (u"", 0))
+
+    def test_raw_encode(self):
+        encode = codecs.raw_unicode_escape_encode
+        for b in range(256):
+            self.assertEqual(encode(unichr(b)), (chr(b), 1))
+
+    def test_raw_decode(self):
+        decode = codecs.raw_unicode_escape_decode
+        for b in range(256):
+            self.assertEqual(decode(chr(b) + '0'), (unichr(b) + u'0', 2))
+
+    def test_escape_encode(self):
+        encode = codecs.raw_unicode_escape_encode
+        check = coding_checker(self, encode)
+        for b in range(256):
+            if chr(b) not in 'uU':
+                check(u'\\' + unichr(b), '\\' + chr(b))
+        check(u'\u20ac', r'\u20ac')
+        check(u'\U0001d120', r'\U0001d120')
+
+    def test_escape_decode(self):
+        decode = codecs.raw_unicode_escape_decode
+        check = coding_checker(self, decode)
+        for b in range(256):
+            if chr(b) not in 'uU':
+                check('\\' + chr(b), u'\\' + unichr(b))
+        check(r"\u20ac", u"\u20ac")
+        check(r"\U0001d120", u"\U0001d120")
+
+    def test_decode_errors(self):
+        decode = codecs.raw_unicode_escape_decode
+        for c, d in ('u', 4), ('U', 4):
+            for i in range(d):
+                self.assertRaises(UnicodeDecodeError, decode,
+                                  "\\" + c + "0"*i)
+                self.assertRaises(UnicodeDecodeError, decode,
+                                  "[\\" + c + "0"*i + "]")
+                data = "[\\" + c + "0"*i + "]\\" + c + "0"*i
+                self.assertEqual(decode(data, "ignore"), (u"[]", len(data)))
+                self.assertEqual(decode(data, "replace"),
+                                 (u"[\ufffd]\ufffd", len(data)))
+        self.assertRaises(UnicodeDecodeError, decode, r"\U00110000")
+        self.assertEqual(decode(r"\U00110000", "ignore"), (u"", 10))
+        self.assertEqual(decode(r"\U00110000", "replace"), (u"\ufffd", 10))
+
+
 class BomTest(unittest.TestCase):
     def test_seek0(self):
         data = u"1234567890"
@@ -1620,6 +2008,8 @@
         BasicStrTest,
         CharmapTest,
         WithStmtTest,
+        UnicodeEscapeTest,
+        RawUnicodeEscapeTest,
         BomTest,
     )
 
diff --git a/lib-python/2.7/test/test_codeop.py b/lib-python/2.7/test/test_codeop.py
--- a/lib-python/2.7/test/test_codeop.py
+++ b/lib-python/2.7/test/test_codeop.py
@@ -50,7 +50,7 @@
         '''succeed iff str is the start of an invalid piece of code'''
         try:
             compile_command(str,symbol=symbol)
-            self.fail("No exception thrown for invalid code")
+            self.fail("No exception raised for invalid code")
         except SyntaxError:
             self.assertTrue(is_syntax)
         except OverflowError:
diff --git a/lib-python/2.7/test/test_compile.py b/lib-python/2.7/test/test_compile.py
--- a/lib-python/2.7/test/test_compile.py
+++ b/lib-python/2.7/test/test_compile.py
@@ -61,6 +61,34 @@
         except SyntaxError:
             pass
 
+    def test_exec_functional_style(self):
+        # Exec'ing a tuple of length 2 works.
+        g = {'b': 2}
+        exec("a = b + 1", g)
+        self.assertEqual(g['a'], 3)
+
+        # As does exec'ing a tuple of length 3.
+        l = {'b': 3}
+        g = {'b': 5, 'c': 7}
+        exec("a = b + c", g, l)
+        self.assertNotIn('a', g)
+        self.assertEqual(l['a'], 10)
+
+        # Tuples not of length 2 or 3 are invalid.
+        with self.assertRaises(TypeError):
+            exec("a = b + 1",)
+
+        with self.assertRaises(TypeError):
+            exec("a = b + 1", {}, {}, {})
+
+        # Can't mix and match the two calling forms.
+        g = {'a': 3, 'b': 4}
+        l = {}
+        with self.assertRaises(TypeError):
+            exec("a = b + 1", g) in g
+        with self.assertRaises(TypeError):
+            exec("a = b + 1", g, l) in g, l
+
     def test_exec_with_general_mapping_for_locals(self):
 
         class M:
diff --git a/lib-python/2.7/test/test_cookie.py b/lib-python/2.7/test/test_cookie.py
--- a/lib-python/2.7/test/test_cookie.py
+++ b/lib-python/2.7/test/test_cookie.py
@@ -64,13 +64,13 @@
 
         # loading 'expires'
         C = Cookie.SimpleCookie()
-        C.load('Customer="W"; expires=Wed, 01-Jan-2010 00:00:00 GMT')
+        C.load('Customer="W"; expires=Wed, 01 Jan 2010 00:00:00 GMT')
         self.assertEqual(C['Customer']['expires'],
-                         'Wed, 01-Jan-2010 00:00:00 GMT')
+                         'Wed, 01 Jan 2010 00:00:00 GMT')
         C = Cookie.SimpleCookie()
-        C.load('Customer="W"; expires=Wed, 01-Jan-98 00:00:00 GMT')
+        C.load('Customer="W"; expires=Wed, 01 Jan 98 00:00:00 GMT')
         self.assertEqual(C['Customer']['expires'],
-                         'Wed, 01-Jan-98 00:00:00 GMT')
+                         'Wed, 01 Jan 98 00:00:00 GMT')
 
     def test_extended_encode(self):
         # Issue 9824: some browsers don't follow the standard; we now
@@ -90,9 +90,10 @@
 
 def test_main():
     run_unittest(CookieTests)
-    with check_warnings(('.+Cookie class is insecure; do not use it',
-                         DeprecationWarning)):
-        run_doctest(Cookie)
+    if Cookie.__doc__ is not None:
+        with check_warnings(('.+Cookie class is insecure; do not use it',
+                             DeprecationWarning)):
+            run_doctest(Cookie)
 
 if __name__ == '__main__':
     test_main()
diff --git a/lib-python/2.7/test/test_cpickle.py b/lib-python/2.7/test/test_cpickle.py
--- a/lib-python/2.7/test/test_cpickle.py
+++ b/lib-python/2.7/test/test_cpickle.py
@@ -1,7 +1,9 @@
 import cPickle, unittest
 from cStringIO import StringIO
-from test.pickletester import AbstractPickleTests, AbstractPickleModuleTests
-from test.pickletester import AbstractPicklerUnpicklerObjectTests
+from test.pickletester import (AbstractPickleTests,
+                               AbstractPickleModuleTests,
+                               AbstractPicklerUnpicklerObjectTests,
+                               BigmemPickleTests)
 from test import test_support
 
 class cPickleTests(AbstractPickleTests, AbstractPickleModuleTests):
@@ -101,6 +103,16 @@
     pickler_class = cPickle.Pickler
     unpickler_class = cPickle.Unpickler
 
+class cPickleBigmemPickleTests(BigmemPickleTests):
+
+    def dumps(self, arg, proto=0, fast=0):
+        # Ignore fast
+        return cPickle.dumps(arg, proto)
+
+    def loads(self, buf):
+        # Ignore fast
+        return cPickle.loads(buf)
+
 
 class Node(object):
     pass
@@ -133,6 +145,7 @@
         cPickleFastPicklerTests,
         cPickleDeepRecursive,
         cPicklePicklerUnpicklerObjectTests,
+        cPickleBigmemPickleTests,
     )
 
 if __name__ == "__main__":
diff --git a/lib-python/2.7/test/test_csv.py b/lib-python/2.7/test/test_csv.py
--- a/lib-python/2.7/test/test_csv.py
+++ b/lib-python/2.7/test/test_csv.py
@@ -243,6 +243,15 @@
         self.assertRaises(csv.Error, self._read_test, ['a,b\nc,d'], [])
         self.assertRaises(csv.Error, self._read_test, ['a,b\r\nc,d'], [])
 
+    def test_read_eof(self):
+        self._read_test(['a,"'], [['a', '']])
+        self._read_test(['"a'], [['a']])
+        self._read_test(['^'], [['\n']], escapechar='^')
+        self.assertRaises(csv.Error, self._read_test, ['a,"'], [], strict=True)
+        self.assertRaises(csv.Error, self._read_test, ['"a'], [], strict=True)
+        self.assertRaises(csv.Error, self._read_test,
+                          ['^'], [], escapechar='^', strict=True)
+
     def test_read_escape(self):
         self._read_test(['a,\\b,c'], [['a', 'b', 'c']], escapechar='\\')
         self._read_test(['a,b\\,c'], [['a', 'b,c']], escapechar='\\')
diff --git a/lib-python/2.7/test/test_decimal.py b/lib-python/2.7/test/test_decimal.py
--- a/lib-python/2.7/test/test_decimal.py
+++ b/lib-python/2.7/test/test_decimal.py
@@ -1448,6 +1448,18 @@
         self.assertEqual(float(d1), 66)
         self.assertEqual(float(d2), 15.32)
 
+    def test_nan_to_float(self):
+        # Test conversions of decimal NANs to float.
+        # See http://bugs.python.org/issue15544
+        for s in ('nan', 'nan1234', '-nan', '-nan2468'):
+            f = float(Decimal(s))
+            self.assertTrue(math.isnan(f))
+
+    def test_snan_to_float(self):
+        for s in ('snan', '-snan', 'snan1357', '-snan1234'):
+            d = Decimal(s)
+            self.assertRaises(ValueError, float, d)
+
     def test_eval_round_trip(self):
 
         #with zero
diff --git a/lib-python/2.7/test/test_deque.py b/lib-python/2.7/test/test_deque.py
--- a/lib-python/2.7/test/test_deque.py
+++ b/lib-python/2.7/test/test_deque.py
@@ -6,6 +6,7 @@
 import copy
 import cPickle as pickle
 import random
+import struct
 
 BIG = 100000
 
@@ -517,6 +518,21 @@
             gc.collect()
             self.assertTrue(ref() is None, "Cycle was not collected")
 
+    check_sizeof = test_support.check_sizeof
+
+    @test_support.cpython_only
+    def test_sizeof(self):
+        BLOCKLEN = 62
+        basesize = test_support.calcobjsize('2P4PlP')
+        blocksize = struct.calcsize('2P%dP' % BLOCKLEN)
+        self.assertEqual(object.__sizeof__(deque()), basesize)
+        check = self.check_sizeof
+        check(deque(), basesize + blocksize)
+        check(deque('a'), basesize + blocksize)
+        check(deque('a' * (BLOCKLEN // 2)), basesize + blocksize)
+        check(deque('a' * (BLOCKLEN // 2 + 1)), basesize + 2 * blocksize)
+        check(deque('a' * (42 * BLOCKLEN)), basesize + 43 * blocksize)
+
 class TestVariousIteratorArgs(unittest.TestCase):
 
     def test_constructor(self):
diff --git a/lib-python/2.7/test/test_descr.py b/lib-python/2.7/test/test_descr.py
--- a/lib-python/2.7/test/test_descr.py
+++ b/lib-python/2.7/test/test_descr.py
@@ -1419,6 +1419,22 @@
         self.assertEqual(x, spam.spamlist)
         self.assertEqual(a, a1)
         self.assertEqual(d, d1)
+        spam_cm = spam.spamlist.__dict__['classmeth']
+        x2, a2, d2 = spam_cm(spam.spamlist, *a, **d)
+        self.assertEqual(x2, spam.spamlist)
+        self.assertEqual(a2, a1)
+        self.assertEqual(d2, d1)
+        class SubSpam(spam.spamlist): pass
+        x2, a2, d2 = spam_cm(SubSpam, *a, **d)
+        self.assertEqual(x2, SubSpam)
+        self.assertEqual(a2, a1)
+        self.assertEqual(d2, d1)
+        with self.assertRaises(TypeError):
+            spam_cm()
+        with self.assertRaises(TypeError):
+            spam_cm(spam.spamlist())
+        with self.assertRaises(TypeError):
+            spam_cm(list)
 
     def test_staticmethods(self):
         # Testing static methods...
@@ -4591,7 +4607,15 @@
             pass
         Foo.__repr__ = Foo.__str__
         foo = Foo()
-        str(foo)
+        self.assertRaises(RuntimeError, str, foo)
+        self.assertRaises(RuntimeError, repr, foo)
+
+    def test_mixing_slot_wrappers(self):
+        class X(dict):
+            __setattr__ = dict.__setitem__
+        x = X()
+        x.y = 42
+        self.assertEqual(x["y"], 42)
 
     def test_cycle_through_dict(self):
         # See bug #1469629
diff --git a/lib-python/2.7/test/test_dict.py b/lib-python/2.7/test/test_dict.py
--- a/lib-python/2.7/test/test_dict.py
+++ b/lib-python/2.7/test/test_dict.py
@@ -254,6 +254,14 @@
         d = dict(zip(range(6), range(6)))
         self.assertEqual(dict.fromkeys(d, 0), dict(zip(range(6), [0]*6)))
 
+        class baddict3(dict):
+            def __new__(cls):
+                return d
+        d = {i : i for i in range(10)}
+        res = d.copy()
+        res.update(a=None, b=None, c=None)
+        self.assertEqual(baddict3.fromkeys({"a", "b", "c"}), res)
+
     def test_copy(self):
         d = {1:1, 2:2, 3:3}
         self.assertEqual(d.copy(), {1:1, 2:2, 3:3})
diff --git a/lib-python/2.7/test/test_dictcomps.py b/lib-python/2.7/test/test_dictcomps.py
--- a/lib-python/2.7/test/test_dictcomps.py
+++ b/lib-python/2.7/test/test_dictcomps.py
@@ -1,54 +1,91 @@
+import unittest
 
-doctests = """
+from test import test_support as support
 
-    >>> k = "old value"
-    >>> { k: None for k in range(10) }
-    {0: None, 1: None, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None}
-    >>> k
-    'old value'
+# For scope testing.
+g = "Global variable"
 
-    >>> { k: k+10 for k in range(10) }
-    {0: 10, 1: 11, 2: 12, 3: 13, 4: 14, 5: 15, 6: 16, 7: 17, 8: 18, 9: 19}
 
-    >>> g = "Global variable"
-    >>> { k: g for k in range(10) }
-    {0: 'Global variable', 1: 'Global variable', 2: 'Global variable', 3: 'Global variable', 4: 'Global variable', 5: 'Global variable', 6: 'Global variable', 7: 'Global variable', 8: 'Global variable', 9: 'Global variable'}
+class DictComprehensionTest(unittest.TestCase):
 
-    >>> { k: v for k in range(10) for v in range(10) if k == v }
-    {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
+    def test_basics(self):
+        expected = {0: 10, 1: 11, 2: 12, 3: 13, 4: 14, 5: 15, 6: 16, 7: 17,
+                    8: 18, 9: 19}
+        actual = {k: k + 10 for k in range(10)}
+        self.assertEqual(actual, expected)
 
-    >>> { k: v for v in range(10) for k in range(v*9, v*10) }
-    {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4, 38: 4, 39: 4, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 54: 6, 55: 6, 56: 6, 57: 6, 58: 6, 59: 6, 63: 7, 64: 7, 65: 7, 66: 7, 67: 7, 68: 7, 69: 7, 72: 8, 73: 8, 74: 8, 75: 8, 76: 8, 77: 8, 78: 8, 79: 8, 81: 9, 82: 9, 83: 9, 84: 9, 85: 9, 86: 9, 87: 9, 88: 9, 89: 9}
+        expected = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9}
+        actual = {k: v for k in range(10) for v in range(10) if k == v}
+        self.assertEqual(actual, expected)
 
-    >>> { x: y for y, x in ((1, 2), (3, 4)) } = 5 # doctest: +IGNORE_EXCEPTION_DETAIL
-    Traceback (most recent call last):
-       ...
-    SyntaxError: ...
+    def test_scope_isolation(self):
+        k = "Local Variable"
 
-    >>> { x: y for y, x in ((1, 2), (3, 4)) } += 5 # doctest: +IGNORE_EXCEPTION_DETAIL
-    Traceback (most recent call last):
-       ...
-    SyntaxError: ...
+        expected = {0: None, 1: None, 2: None, 3: None, 4: None, 5: None,
+                    6: None, 7: None, 8: None, 9: None}
+        actual = {k: None for k in range(10)}
+        self.assertEqual(actual, expected)
+        self.assertEqual(k, "Local Variable")
 
-"""
+        expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
+                    38: 4, 39: 4, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 54: 6,
+                    55: 6, 56: 6, 57: 6, 58: 6, 59: 6, 63: 7, 64: 7, 65: 7,
+                    66: 7, 67: 7, 68: 7, 69: 7, 72: 8, 73: 8, 74: 8, 75: 8,
+                    76: 8, 77: 8, 78: 8, 79: 8, 81: 9, 82: 9, 83: 9, 84: 9,
+                    85: 9, 86: 9, 87: 9, 88: 9, 89: 9}
+        actual = {k: v for v in range(10) for k in range(v * 9, v * 10)}
+        self.assertEqual(k, "Local Variable")
+        self.assertEqual(actual, expected)
 
-__test__ = {'doctests' : doctests}
+    def test_scope_isolation_from_global(self):
+        expected = {0: None, 1: None, 2: None, 3: None, 4: None, 5: None,
+                    6: None, 7: None, 8: None, 9: None}
+        actual = {g: None for g in range(10)}
+        self.assertEqual(actual, expected)
+        self.assertEqual(g, "Global variable")
 
-def test_main(verbose=None):
-    import sys
-    from test import test_support
-    from test import test_dictcomps
-    test_support.run_doctest(test_dictcomps, verbose)
+        expected = {9: 1, 18: 2, 19: 2, 27: 3, 28: 3, 29: 3, 36: 4, 37: 4,
+                    38: 4, 39: 4, 45: 5, 46: 5, 47: 5, 48: 5, 49: 5, 54: 6,
+                    55: 6, 56: 6, 57: 6, 58: 6, 59: 6, 63: 7, 64: 7, 65: 7,
+                    66: 7, 67: 7, 68: 7, 69: 7, 72: 8, 73: 8, 74: 8, 75: 8,
+                    76: 8, 77: 8, 78: 8, 79: 8, 81: 9, 82: 9, 83: 9, 84: 9,
+                    85: 9, 86: 9, 87: 9, 88: 9, 89: 9}
+        actual = {g: v for v in range(10) for g in range(v * 9, v * 10)}
+        self.assertEqual(g, "Global variable")
+        self.assertEqual(actual, expected)
 
-    # verify reference counting
-    if verbose and hasattr(sys, "gettotalrefcount"):
-        import gc
-        counts = [None] * 5
-        for i in range(len(counts)):
-            test_support.run_doctest(test_dictcomps, verbose)
-            gc.collect()
-            counts[i] = sys.gettotalrefcount()
-        print(counts)
+    def test_global_visibility(self):
+        expected = {0: 'Global variable', 1: 'Global variable',
+                    2: 'Global variable', 3: 'Global variable',
+                    4: 'Global variable', 5: 'Global variable',
+                    6: 'Global variable', 7: 'Global variable',
+                    8: 'Global variable', 9: 'Global variable'}
+        actual = {k: g for k in range(10)}
+        self.assertEqual(actual, expected)
+
+    def test_local_visibility(self):
+        v = "Local variable"
+        expected = {0: 'Local variable', 1: 'Local variable',
+                    2: 'Local variable', 3: 'Local variable',
+                    4: 'Local variable', 5: 'Local variable',
+                    6: 'Local variable', 7: 'Local variable',
+                    8: 'Local variable', 9: 'Local variable'}
+        actual = {k: v for k in range(10)}
+        self.assertEqual(actual, expected)
+        self.assertEqual(v, "Local variable")
+
+    def test_illegal_assignment(self):
+        with self.assertRaisesRegexp(SyntaxError, "can't assign"):
+            compile("{x: y for y, x in ((1, 2), (3, 4))} = 5", "<test>",
+                    "exec")
+
+        with self.assertRaisesRegexp(SyntaxError, "can't assign"):
+            compile("{x: y for y, x in ((1, 2), (3, 4))} += 5", "<test>",
+                    "exec")
+
+
+def test_main():
+    support.run_unittest(__name__)
 
 if __name__ == "__main__":
-    test_main(verbose=True)
+    test_main()
diff --git a/lib-python/2.7/test/test_doctest.py b/lib-python/2.7/test/test_doctest.py
--- a/lib-python/2.7/test/test_doctest.py
+++ b/lib-python/2.7/test/test_doctest.py
@@ -2006,6 +2006,31 @@
          >>> suite.run(unittest.TestResult())
          <unittest.result.TestResult run=9 errors=0 failures=4>
 
+       The module need not contain any doctest examples:
+
+         >>> suite = doctest.DocTestSuite('test.sample_doctest_no_doctests')
+         >>> suite.run(unittest.TestResult())
+         <unittest.result.TestResult run=0 errors=0 failures=0>
+
+       However, if DocTestSuite finds no docstrings, it raises an error:
+
+         >>> try:
+         ...     doctest.DocTestSuite('test.sample_doctest_no_docstrings')
+         ... except ValueError as e:
+         ...     error = e
+
+         >>> print(error.args[1])
+         has no docstrings
+
+       You can prevent this error by passing a DocTestFinder instance with
+       the `exclude_empty` keyword argument set to False:
+
+         >>> finder = doctest.DocTestFinder(exclude_empty=False)
+         >>> suite = doctest.DocTestSuite('test.sample_doctest_no_docstrings',
+         ...                              test_finder=finder)
+         >>> suite.run(unittest.TestResult())
+         <unittest.result.TestResult run=0 errors=0 failures=0>
+
        We can use the current module:
 
          >>> suite = test.sample_doctest.test_suite()
@@ -2648,7 +2673,9 @@
     from test import test_doctest
 
     # Ignore all warnings about the use of class Tester in this module.
-    deprecations = [("class Tester is deprecated", DeprecationWarning)]
+    deprecations = []
+    if __debug__:
+        deprecations.append(("class Tester is deprecated", DeprecationWarning))
     if sys.py3kwarning:
         deprecations += [("backquote not supported", SyntaxWarning),
                          ("execfile.. not supported", DeprecationWarning)]
diff --git a/lib-python/2.7/test/test_docxmlrpc.py b/lib-python/2.7/test/test_docxmlrpc.py
--- a/lib-python/2.7/test/test_docxmlrpc.py
+++ b/lib-python/2.7/test/test_docxmlrpc.py
@@ -100,7 +100,7 @@
         self.assertEqual(response.status, 200)
         self.assertEqual(response.getheader("Content-type"), "text/html")
 
-        # Server throws an exception if we don't start to read the data
+        # Server raises an exception if we don't start to read the data
         response.read()
 
     def test_invalid_get_response(self):
diff --git a/lib-python/2.7/test/test_email.py b/lib-python/2.7/test/test_email.py
--- a/lib-python/2.7/test/test_email.py
+++ b/lib-python/2.7/test/test_email.py
@@ -3,10 +3,12 @@
 
 # The specific tests now live in Lib/email/test
 from email.test.test_email import suite
+from email.test.test_email_renamed import suite as suite2
 from test import test_support
 
 def test_main():
     test_support.run_unittest(suite())
+    test_support.run_unittest(suite2())
 
 if __name__ == '__main__':
     test_main()
diff --git a/lib-python/2.7/test/test_exceptions.py b/lib-python/2.7/test/test_exceptions.py
--- a/lib-python/2.7/test/test_exceptions.py
+++ b/lib-python/2.7/test/test_exceptions.py
@@ -479,6 +479,18 @@
         except AssertionError as e:
             self.assertEqual(str(e), "(3,)")
 
+    def test_bad_exception_clearing(self):
+        # See issue 16445: use of Py_XDECREF instead of Py_CLEAR in
+        # BaseException_set_message gave a possible way to segfault the
+        # interpreter.
+        class Nasty(str):
+            def __del__(message):
+                del e.message
+
+        e = ValueError(Nasty("msg"))
+        e.args = ()
+        del e.message
+
 
 # Helper class used by TestSameStrAndUnicodeMsg
 class ExcWithOverriddenStr(Exception):
diff --git a/lib-python/2.7/test/test_fcntl.py b/lib-python/2.7/test/test_fcntl.py
--- a/lib-python/2.7/test/test_fcntl.py
+++ b/lib-python/2.7/test/test_fcntl.py
@@ -6,6 +6,7 @@
 import os
 import struct
 import sys
+import _testcapi
 import unittest
 from test.test_support import (verbose, TESTFN, unlink, run_unittest,
     import_module)
@@ -81,6 +82,26 @@
             rv = fcntl.fcntl(self.f, fcntl.F_SETLKW, lockdata)
         self.f.close()
 
+    def test_fcntl_bad_file(self):
+        class F:
+            def __init__(self, fn):
+                self.fn = fn
+            def fileno(self):
+                return self.fn
+        self.assertRaises(ValueError, fcntl.fcntl, -1, fcntl.F_SETFL, os.O_NONBLOCK)
+        self.assertRaises(ValueError, fcntl.fcntl, F(-1), fcntl.F_SETFL, os.O_NONBLOCK)
+        self.assertRaises(TypeError, fcntl.fcntl, 'spam', fcntl.F_SETFL, os.O_NONBLOCK)
+        self.assertRaises(TypeError, fcntl.fcntl, F('spam'), fcntl.F_SETFL, os.O_NONBLOCK)
+        # Issue 15989
+        self.assertRaises(ValueError, fcntl.fcntl, _testcapi.INT_MAX + 1,
+                                                   fcntl.F_SETFL, os.O_NONBLOCK)
+        self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MAX + 1),
+                                                   fcntl.F_SETFL, os.O_NONBLOCK)
+        self.assertRaises(ValueError, fcntl.fcntl, _testcapi.INT_MIN - 1,
+                                                   fcntl.F_SETFL, os.O_NONBLOCK)
+        self.assertRaises(ValueError, fcntl.fcntl, F(_testcapi.INT_MIN - 1),
+                                                   fcntl.F_SETFL, os.O_NONBLOCK)
+
     def test_fcntl_64_bit(self):
         # Issue #1309352: fcntl shouldn't fail when the third arg fits in a
         # C 'long' but not in a C 'int'.
diff --git a/lib-python/2.7/test/test_file2k.py b/lib-python/2.7/test/test_file2k.py
--- a/lib-python/2.7/test/test_file2k.py
+++ b/lib-python/2.7/test/test_file2k.py
@@ -2,6 +2,9 @@
 import os
 import unittest
 import itertools
+import select
+import signal
+import subprocess
 import time
 from array import array
 from weakref import proxy
@@ -602,6 +605,148 @@
         self._test_close_open_io(io_func)
 
 
+ at unittest.skipUnless(os.name == 'posix', 'test requires a posix system.')
+class TestFileSignalEINTR(unittest.TestCase):
+    def _test_reading(self, data_to_write, read_and_verify_code, method_name,
+                      universal_newlines=False):
+        """Generic buffered read method test harness to verify EINTR behavior.
+
+        Also validates that Python signal handlers are run during the read.
+
+        Args:
+            data_to_write: String to write to the child process for reading
+                before sending it a signal, confirming the signal was handled,
+                writing a final newline char and closing the infile pipe.
+            read_and_verify_code: Single "line" of code to read from a file
+                object named 'infile' and validate the result.  This will be
+                executed as part of a python subprocess fed data_to_write.
+            method_name: The name of the read method being tested, for use in
+                an error message on failure.
+            universal_newlines: If True, infile will be opened in universal
+                newline mode in the child process.
+        """
+        if universal_newlines:
+            # Test the \r\n -> \n conversion while we're at it.
+            data_to_write = data_to_write.replace('\n', '\r\n')
+            infile_setup_code = 'infile = os.fdopen(sys.stdin.fileno(), "rU")'
+        else:
+            infile_setup_code = 'infile = sys.stdin'
+        # Total pipe IO in this function is smaller than the minimum posix OS
+        # pipe buffer size of 512 bytes.  No writer should block.
+        assert len(data_to_write) < 512, 'data_to_write must fit in pipe buf.'
+
+        child_code = (
+             'import os, signal, sys ;'
+             'signal.signal('
+                     'signal.SIGINT, lambda s, f: sys.stderr.write("$\\n")) ;'
+             + infile_setup_code + ' ;' +
+             'assert isinstance(infile, file) ;'
+             'sys.stderr.write("Go.\\n") ;'
+             + read_and_verify_code)
+        reader_process = subprocess.Popen(
+                [sys.executable, '-c', child_code],
+                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE)
+        # Wait for the signal handler to be installed.
+        go = reader_process.stderr.read(4)
+        if go != 'Go.\n':
+            reader_process.kill()
+            self.fail('Error from %s process while awaiting "Go":\n%s' % (
+                    method_name, go+reader_process.stderr.read()))
+        reader_process.stdin.write(data_to_write)
+        signals_sent = 0
+        rlist = []
+        # We don't know when the read_and_verify_code in our child is actually
+        # executing within the read system call we want to interrupt.  This
+        # loop waits for a bit before sending the first signal to increase
+        # the likelihood of that.  Implementations without correct EINTR
+        # and signal handling usually fail this test.
+        while not rlist:
+            rlist, _, _ = select.select([reader_process.stderr], (), (), 0.05)
+            reader_process.send_signal(signal.SIGINT)
+            # Give the subprocess time to handle it before we loop around and
+            # send another one.  On OSX the second signal happening close to
+            # immediately after the first was causing the subprocess to crash
+            # via the OS's default SIGINT handler.
+            time.sleep(0.1)
+            signals_sent += 1
+            if signals_sent > 200:
+                reader_process.kill()
+                self.fail("failed to handle signal during %s." % method_name)
+        # This assumes anything unexpected that writes to stderr will also
+        # write a newline.  That is true of the traceback printing code.
+        signal_line = reader_process.stderr.readline()
+        if signal_line != '$\n':
+            reader_process.kill()
+            self.fail('Error from %s process while awaiting signal:\n%s' % (
+                    method_name, signal_line+reader_process.stderr.read()))
+        # We append a newline to our input so that a readline call can
+        # end on its own before the EOF is seen.
+        stdout, stderr = reader_process.communicate(input='\n')
+        if reader_process.returncode != 0:
+            self.fail('%s() process exited rc=%d.\nSTDOUT:\n%s\nSTDERR:\n%s' % (
+                    method_name, reader_process.returncode, stdout, stderr))
+
+    def test_readline(self, universal_newlines=False):
+        """file.readline must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write='hello, world!',
+                read_and_verify_code=(
+                        'line = infile.readline() ;'
+                        'expected_line = "hello, world!\\n" ;'
+                        'assert line == expected_line, ('
+                        '"read %r expected %r" % (line, expected_line))'
+                ),
+                method_name='readline',
+                universal_newlines=universal_newlines)
+
+    def test_readline_with_universal_newlines(self):
+        self.test_readline(universal_newlines=True)
+
+    def test_readlines(self, universal_newlines=False):
+        """file.readlines must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write='hello\nworld!',
+                read_and_verify_code=(
+                        'lines = infile.readlines() ;'
+                        'expected_lines = ["hello\\n", "world!\\n"] ;'
+                        'assert lines == expected_lines, ('
+                        '"readlines returned wrong data.\\n" '
+                        '"got lines %r\\nexpected  %r" '
+                        '% (lines, expected_lines))'
+                ),
+                method_name='readlines',
+                universal_newlines=universal_newlines)
+
+    def test_readlines_with_universal_newlines(self):
+        self.test_readlines(universal_newlines=True)
+
+    def test_readall(self):
+        """Unbounded file.read() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write='hello, world!abcdefghijklm',
+                read_and_verify_code=(
+                        'data = infile.read() ;'
+                        'expected_data = "hello, world!abcdefghijklm\\n";'
+                        'assert data == expected_data, ('
+                        '"read %r expected %r" % (data, expected_data))'
+                ),
+                method_name='unbounded read')
+
+    def test_readinto(self):
+        """file.readinto must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write='hello, world!',
+                read_and_verify_code=(
+                        'data = bytearray(50) ;'
+                        'num_read = infile.readinto(data) ;'
+                        'expected_data = "hello, world!\\n";'
+                        'assert data[:num_read] == expected_data, ('
+                        '"read %r expected %r" % (data, expected_data))'
+                ),
+                method_name='readinto')
+
+
 class StdoutTests(unittest.TestCase):
 
     def test_move_stdout_on_write(self):
@@ -678,7 +823,7 @@
     # So get rid of it no matter what.
     try:
         run_unittest(AutoFileTests, OtherFileTests, FileSubclassTests,
-            FileThreadingTests, StdoutTests)
+            FileThreadingTests, TestFileSignalEINTR, StdoutTests)
     finally:
         if os.path.exists(TESTFN):
             os.unlink(TESTFN)
diff --git a/lib-python/2.7/test/test_file_eintr.py b/lib-python/2.7/test/test_file_eintr.py
new file mode 100644
--- /dev/null
+++ b/lib-python/2.7/test/test_file_eintr.py
@@ -0,0 +1,239 @@
+# Written to test interrupted system calls interfering with our many buffered
+# IO implementations.  http://bugs.python.org/issue12268
+#
+# This tests the '_io' module.  Similar tests for Python 2.x's older
+# default file I/O implementation exist within test_file2k.py.
+#
+# It was suggested that this code could be merged into test_io and the tests
+# made to work using the same method as the existing signal tests in test_io.
+# I was unable to get single process tests using alarm or setitimer that way
+# to reproduce the EINTR problems.  This process based test suite reproduces
+# the problems prior to the issue12268 patch reliably on Linux and OSX.
+#  - gregory.p.smith
+
+import os
+import select
+import signal
+import subprocess
+import sys
+from test.test_support import run_unittest
+import time
+import unittest
+
+# Test import all of the things we're about to try testing up front.
+from _io import FileIO
+
+
+ at unittest.skipUnless(os.name == 'posix', 'tests requires a posix system.')
+class TestFileIOSignalInterrupt(unittest.TestCase):
+    def setUp(self):
+        self._process = None
+
+    def tearDown(self):
+        if self._process and self._process.poll() is None:
+            try:
+                self._process.kill()
+            except OSError:
+                pass
+
+    def _generate_infile_setup_code(self):
+        """Returns the infile = ... line of code for the reader process.
+
+        subclasseses should override this to test different IO objects.
+        """
+        return ('import _io ;'
+                'infile = _io.FileIO(sys.stdin.fileno(), "rb")')
+
+    def fail_with_process_info(self, why, stdout=b'', stderr=b'',
+                               communicate=True):
+        """A common way to cleanup and fail with useful debug output.
+
+        Kills the process if it is still running, collects remaining output
+        and fails the test with an error message including the output.
+
+        Args:
+            why: Text to go after "Error from IO process" in the message.
+            stdout, stderr: standard output and error from the process so
+                far to include in the error message.
+            communicate: bool, when True we call communicate() on the process
+                after killing it to gather additional output.
+        """
+        if self._process.poll() is None:
+            time.sleep(0.1)  # give it time to finish printing the error.
+            try:
+                self._process.terminate()  # Ensure it dies.
+            except OSError:
+                pass
+        if communicate:
+            stdout_end, stderr_end = self._process.communicate()
+            stdout += stdout_end
+            stderr += stderr_end
+        self.fail('Error from IO process %s:\nSTDOUT:\n%sSTDERR:\n%s\n' %
+                  (why, stdout.decode(), stderr.decode()))
+
+    def _test_reading(self, data_to_write, read_and_verify_code):
+        """Generic buffered read method test harness to validate EINTR behavior.
+
+        Also validates that Python signal handlers are run during the read.
+
+        Args:
+            data_to_write: String to write to the child process for reading
+                before sending it a signal, confirming the signal was handled,
+                writing a final newline and closing the infile pipe.
+            read_and_verify_code: Single "line" of code to read from a file
+                object named 'infile' and validate the result.  This will be
+                executed as part of a python subprocess fed data_to_write.
+        """
+        infile_setup_code = self._generate_infile_setup_code()
+        # Total pipe IO in this function is smaller than the minimum posix OS
+        # pipe buffer size of 512 bytes.  No writer should block.
+        assert len(data_to_write) < 512, 'data_to_write must fit in pipe buf.'
+
+        # Start a subprocess to call our read method while handling a signal.
+        self._process = subprocess.Popen(
+                [sys.executable, '-u', '-c',
+                 'import io, signal, sys ;'
+                 'signal.signal(signal.SIGINT, '
+                               'lambda s, f: sys.stderr.write("$\\n")) ;'
+                 + infile_setup_code + ' ;' +
+                 'sys.stderr.write("Worm Sign!\\n") ;'
+                 + read_and_verify_code + ' ;' +
+                 'infile.close()'
+                ],
+                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE)
+
+        # Wait for the signal handler to be installed.
+        worm_sign = self._process.stderr.read(len(b'Worm Sign!\n'))
+        if worm_sign != b'Worm Sign!\n':  # See also, Dune by Frank Herbert.
+            self.fail_with_process_info('while awaiting a sign',
+                                        stderr=worm_sign)
+        self._process.stdin.write(data_to_write)
+
+        signals_sent = 0
+        rlist = []
+        # We don't know when the read_and_verify_code in our child is actually
+        # executing within the read system call we want to interrupt.  This
+        # loop waits for a bit before sending the first signal to increase
+        # the likelihood of that.  Implementations without correct EINTR
+        # and signal handling usually fail this test.
+        while not rlist:
+            rlist, _, _ = select.select([self._process.stderr], (), (), 0.05)
+            self._process.send_signal(signal.SIGINT)
+            signals_sent += 1
+            if signals_sent > 200:
+                self._process.kill()
+                self.fail('reader process failed to handle our signals.')
+        # This assumes anything unexpected that writes to stderr will also
+        # write a newline.  That is true of the traceback printing code.
+        signal_line = self._process.stderr.readline()
+        if signal_line != b'$\n':
+            self.fail_with_process_info('while awaiting signal',
+                                        stderr=signal_line)
+
+        # We append a newline to our input so that a readline call can
+        # end on its own before the EOF is seen and so that we're testing
+        # the read call that was interrupted by a signal before the end of
+        # the data stream has been reached.
+        stdout, stderr = self._process.communicate(input=b'\n')
+        if self._process.returncode:
+            self.fail_with_process_info(
+                    'exited rc=%d' % self._process.returncode,
+                    stdout, stderr, communicate=False)
+        # PASS!
+
+    # String format for the read_and_verify_code used by read methods.
+    _READING_CODE_TEMPLATE = (
+            'got = infile.{read_method_name}() ;'
+            'expected = {expected!r} ;'
+            'assert got == expected, ('
+                    '"{read_method_name} returned wrong data.\\n"'
+                    '"got data %r\\nexpected %r" % (got, expected))'
+            )
+
+    def test_readline(self):
+        """readline() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write=b'hello, world!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='readline',
+                        expected=b'hello, world!\n'))
+
+    def test_readlines(self):
+        """readlines() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write=b'hello\nworld!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='readlines',
+                        expected=[b'hello\n', b'world!\n']))
+
+    def test_readall(self):
+        """readall() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write=b'hello\nworld!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='readall',
+                        expected=b'hello\nworld!\n'))
+        # read() is the same thing as readall().
+        self._test_reading(
+                data_to_write=b'hello\nworld!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='read',
+                        expected=b'hello\nworld!\n'))
+
+
+class TestBufferedIOSignalInterrupt(TestFileIOSignalInterrupt):
+    def _generate_infile_setup_code(self):
+        """Returns the infile = ... line of code to make a BufferedReader."""
+        return ('infile = io.open(sys.stdin.fileno(), "rb") ;'
+                'import _io ;assert isinstance(infile, _io.BufferedReader)')
+
+    def test_readall(self):
+        """BufferedReader.read() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write=b'hello\nworld!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='read',
+                        expected=b'hello\nworld!\n'))
+
+
+class TestTextIOSignalInterrupt(TestFileIOSignalInterrupt):
+    def _generate_infile_setup_code(self):
+        """Returns the infile = ... line of code to make a TextIOWrapper."""
+        return ('infile = io.open(sys.stdin.fileno(), "rt", newline=None) ;'
+                'import _io ;assert isinstance(infile, _io.TextIOWrapper)')
+
+    def test_readline(self):
+        """readline() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write=b'hello, world!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='readline',
+                        expected='hello, world!\n'))
+
+    def test_readlines(self):
+        """readlines() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write=b'hello\r\nworld!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='readlines',
+                        expected=['hello\n', 'world!\n']))
+
+    def test_readall(self):
+        """read() must handle signals and not lose data."""
+        self._test_reading(
+                data_to_write=b'hello\nworld!',
+                read_and_verify_code=self._READING_CODE_TEMPLATE.format(
+                        read_method_name='read',
+                        expected="hello\nworld!\n"))
+
+
+def test_main():
+    test_cases = [
+            tc for tc in globals().values()
+            if isinstance(tc, type) and issubclass(tc, unittest.TestCase)]
+    run_unittest(*test_cases)
+
+
+if __name__ == '__main__':
+    test_main()
diff --git a/lib-python/2.7/test/test_fileio.py b/lib-python/2.7/test/test_fileio.py
--- a/lib-python/2.7/test/test_fileio.py
+++ b/lib-python/2.7/test/test_fileio.py
@@ -9,6 +9,8 @@
 from array import array
 from weakref import proxy
 from functools import wraps
+from UserList import UserList
+import _testcapi
 
 from test.test_support import TESTFN, check_warnings, run_unittest, make_bad_fd
 from test.test_support import py3k_bytes as bytes
@@ -71,6 +73,26 @@
         n = self.f.readinto(a)
         self.assertEqual(array(b'b', [1, 2]), a[:n])
 
+    def testWritelinesList(self):
+        l = [b'123', b'456']
+        self.f.writelines(l)
+        self.f.close()
+        self.f = _FileIO(TESTFN, 'rb')
+        buf = self.f.read()
+        self.assertEqual(buf, b'123456')
+
+    def testWritelinesUserList(self):
+        l = UserList([b'123', b'456'])
+        self.f.writelines(l)
+        self.f.close()
+        self.f = _FileIO(TESTFN, 'rb')
+        buf = self.f.read()
+        self.assertEqual(buf, b'123456')
+
+    def testWritelinesError(self):
+        self.assertRaises(TypeError, self.f.writelines, [1, 2, 3])
+        self.assertRaises(TypeError, self.f.writelines, None)
+
     def test_none_args(self):
         self.f.write(b"hi\nbye\nabc")
         self.f.close()
@@ -130,6 +152,14 @@
         else:
             self.fail("Should have raised IOError")
 
+    @unittest.skipIf(os.name == 'nt', "test only works on a POSIX-like system")
+    def testOpenDirFD(self):
+        fd = os.open('.', os.O_RDONLY)
+        with self.assertRaises(IOError) as cm:
+            _FileIO(fd, 'r')
+        os.close(fd)
+        self.assertEqual(cm.exception.errno, errno.EISDIR)
+
     #A set of functions testing that we get expected behaviour if someone has
     #manually closed the internal file descriptor.  First, a decorator:
     def ClosedFD(func):
@@ -314,6 +344,9 @@
         if sys.platform == 'win32':
             import msvcrt
             self.assertRaises(IOError, msvcrt.get_osfhandle, make_bad_fd())
+        # Issue 15989
+        self.assertRaises(TypeError, _FileIO, _testcapi.INT_MAX + 1)
+        self.assertRaises(TypeError, _FileIO, _testcapi.INT_MIN - 1)
 
     def testBadModeArgument(self):
         # verify that we get a sensible error message for bad mode argument
@@ -417,10 +450,22 @@
         env = dict(os.environ)
         env[b'LC_CTYPE'] = b'C'
         _, out = run_python('-c', 'import _io; _io.FileIO(%r)' % filename, env=env)
-        if ('UnicodeEncodeError' not in out and
-            'IOError: [Errno 2] No such file or directory' not in out):
+        if ('UnicodeEncodeError' not in out and not
+                ( ('IOError: [Errno 2] No such file or directory' in out) or
+                  ('IOError: [Errno 22] Invalid argument' in out) ) ):
             self.fail('Bad output: %r' % out)
 
+    def testUnclosedFDOnException(self):
+        class MyException(Exception): pass
+        class MyFileIO(_FileIO):
+            def __setattr__(self, name, value):
+                if name == "name":
+                    raise MyException("blocked setting name")
+                return super(MyFileIO, self).__setattr__(name, value)
+        fd = os.open(__file__, os.O_RDONLY)
+        self.assertRaises(MyException, MyFileIO, fd)
+        os.close(fd)  # should not raise OSError(EBADF)
+
 def test_main():
     # Historically, these tests have been sloppy about removing TESTFN.
     # So get rid of it no matter what.
diff --git a/lib-python/2.7/test/test_format.py b/lib-python/2.7/test/test_format.py
--- a/lib-python/2.7/test/test_format.py
+++ b/lib-python/2.7/test/test_format.py
@@ -234,6 +234,16 @@
         testformat('%g', 1.1, '1.1')
         testformat('%#g', 1.1, '1.10000')
 
+        # Regression test for http://bugs.python.org/issue15516.
+        class IntFails(object):
+            def __int__(self):
+                raise TestFailed
+            def __long__(self):
+                return 0
+
+        fst = IntFails()
+        testformat("%x", fst, '0')
+
         # Test exception for unknown format characters
         if verbose:
             print 'Testing exceptions'
diff --git a/lib-python/2.7/test/test_functools.py b/lib-python/2.7/test/test_functools.py
--- a/lib-python/2.7/test/test_functools.py
+++ b/lib-python/2.7/test/test_functools.py
@@ -151,6 +151,23 @@
         f_copy = pickle.loads(pickle.dumps(f))
         self.assertEqual(signature(f), signature(f_copy))
 
+    # Issue 6083: Reference counting bug
+    def test_setstate_refcount(self):
+        class BadSequence:
+            def __len__(self):
+                return 4
+            def __getitem__(self, key):
+                if key == 0:
+                    return max
+                elif key == 1:
+                    return tuple(range(1000000))
+                elif key in (2, 3):
+                    return {}
+                raise IndexError
+
+        f = self.thetype(object)
+        self.assertRaises(SystemError, f.__setstate__, BadSequence())
+
 class PartialSubclass(functools.partial):
     pass
 
@@ -164,6 +181,7 @@
 
     # the python version isn't picklable
     def test_pickle(self): pass
+    def test_setstate_refcount(self): pass
 
 class TestUpdateWrapper(unittest.TestCase):
 
@@ -232,6 +250,7 @@
         self.assertEqual(wrapper.attr, 'This is a different test')
         self.assertEqual(wrapper.dict_attr, f.dict_attr)
 
+    @test_support.requires_docstrings
     def test_builtin_update(self):
         # Test for bug #1576241
         def wrapper():
@@ -258,7 +277,7 @@
         self.assertEqual(wrapper.__name__, 'f')
         self.assertEqual(wrapper.attr, 'This is also a test')
 
-    @unittest.skipIf(not sys.flags.optimize <= 1,
+    @unittest.skipIf(sys.flags.optimize >= 2,
                      "Docstrings are omitted with -O2 and above")
     def test_default_update_doc(self):
         wrapper = self._default_update()
diff --git a/lib-python/2.7/test/test_gc.py b/lib-python/2.7/test/test_gc.py
--- a/lib-python/2.7/test/test_gc.py
+++ b/lib-python/2.7/test/test_gc.py
@@ -1,9 +1,15 @@
 import unittest
 from test.test_support import verbose, run_unittest
 import sys
+import time
 import gc
 import weakref
 
+try:
+    import threading
+except ImportError:
+    threading = None
+
 ### Support code
 ###############################################################################
 
@@ -299,6 +305,69 @@
                 v = {1: v, 2: Ouch()}
         gc.disable()
 
+    @unittest.skipUnless(threading, "test meaningless on builds without threads")
+    def test_trashcan_threads(self):
+        # Issue #13992: trashcan mechanism should be thread-safe
+        NESTING = 60
+        N_THREADS = 2
+
+        def sleeper_gen():
+            """A generator that releases the GIL when closed or dealloc'ed."""
+            try:
+                yield
+            finally:
+                time.sleep(0.000001)
+
+        class C(list):
+            # Appending to a list is atomic, which avoids the use of a lock.
+            inits = []
+            dels = []
+            def __init__(self, alist):
+                self[:] = alist
+                C.inits.append(None)
+            def __del__(self):
+                # This __del__ is called by subtype_dealloc().
+                C.dels.append(None)
+                # `g` will release the GIL when garbage-collected.  This
+                # helps assert subtype_dealloc's behaviour when threads
+                # switch in the middle of it.
+                g = sleeper_gen()
+                next(g)
+                # Now that __del__ is finished, subtype_dealloc will proceed
+                # to call list_dealloc, which also uses the trashcan mechanism.
+
+        def make_nested():
+            """Create a sufficiently nested container object so that the
+            trashcan mechanism is invoked when deallocating it."""
+            x = C([])
+            for i in range(NESTING):
+                x = [C([x])]
+            del x
+
+        def run_thread():
+            """Exercise make_nested() in a loop."""
+            while not exit:
+                make_nested()
+
+        old_checkinterval = sys.getcheckinterval()
+        sys.setcheckinterval(3)
+        try:
+            exit = False
+            threads = []
+            for i in range(N_THREADS):
+                t = threading.Thread(target=run_thread)
+                threads.append(t)
+            for t in threads:
+                t.start()
+            time.sleep(1.0)
+            exit = True
+            for t in threads:
+                t.join()
+        finally:
+            sys.setcheckinterval(old_checkinterval)
+        gc.collect()
+        self.assertEqual(len(C.inits), len(C.dels))
+
     def test_boom(self):
         class Boom:
             def __getattr__(self, someattribute):
diff --git a/lib-python/2.7/test/test_gdb.py b/lib-python/2.7/test/test_gdb.py
--- a/lib-python/2.7/test/test_gdb.py
+++ b/lib-python/2.7/test/test_gdb.py
@@ -19,19 +19,48 @@
     # This is what "no gdb" looks like.  There may, however, be other
     # errors that manifest this way too.
     raise unittest.SkipTest("Couldn't find gdb on the path")
-gdb_version_number = re.search(r"^GNU gdb [^\d]*(\d+)\.", gdb_version)
-if int(gdb_version_number.group(1)) < 7:
+gdb_version_number = re.search("^GNU gdb [^\d]*(\d+)\.(\d)", gdb_version)
+gdb_major_version = int(gdb_version_number.group(1))
+gdb_minor_version = int(gdb_version_number.group(2))
+if gdb_major_version < 7:
     raise unittest.SkipTest("gdb versions before 7.0 didn't support python embedding"
                             " Saw:\n" + gdb_version)
 
+# Location of custom hooks file in a repository checkout.
+checkout_hook_path = os.path.join(os.path.dirname(sys.executable),
+                                  'python-gdb.py')
+
+def run_gdb(*args, **env_vars):
+    """Runs gdb in --batch mode with the additional arguments given by *args.
+
+    Returns its (stdout, stderr)
+    """
+    if env_vars:
+        env = os.environ.copy()
+        env.update(env_vars)
+    else:
+        env = None
+    base_cmd = ('gdb', '--batch')
+    if (gdb_major_version, gdb_minor_version) >= (7, 4):
+        base_cmd += ('-iex', 'add-auto-load-safe-path ' + checkout_hook_path)
+    out, err = subprocess.Popen(base_cmd + args,
+        stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env,
+        ).communicate()
+    return out, err
+
 # Verify that "gdb" was built with the embedded python support enabled:
-cmd = "--eval-command=python import sys; print sys.version_info"
-p = subprocess.Popen(["gdb", "--batch", cmd],
-                     stdout=subprocess.PIPE)
-gdbpy_version, _ = p.communicate()
-if gdbpy_version == '':
+gdbpy_version, _ = run_gdb("--eval-command=python import sys; print sys.version_info")
+if not gdbpy_version:
     raise unittest.SkipTest("gdb not built with embedded python support")
 
+# Verify that "gdb" can load our custom hooks.  In theory this should never
+# fail, but we don't handle the case of the hooks file not existing if the
+# tests are run from an installed Python (we'll produce failures in that case).
+cmd = ['--args', sys.executable]
+_, gdbpy_errors = run_gdb('--args', sys.executable)
+if "auto-loading has been declined" in gdbpy_errors:
+    msg = "gdb security settings prevent use of custom hooks: "
+
 def python_is_optimized():
     cflags = sysconfig.get_config_vars()['PY_CFLAGS']
     final_opt = ""
@@ -42,10 +71,7 @@
 
 def gdb_has_frame_select():
     # Does this build of gdb have gdb.Frame.select ?
-    cmd = "--eval-command=python print(dir(gdb.Frame))"
-    p = subprocess.Popen(["gdb", "--batch", cmd],
-                         stdout=subprocess.PIPE)
-    stdout, _ = p.communicate()
+    stdout, _ = run_gdb("--eval-command=python print(dir(gdb.Frame))")
     m = re.match(r'.*\[(.*)\].*', stdout)
     if not m:
         raise unittest.SkipTest("Unable to parse output from gdb.Frame.select test")
@@ -58,21 +84,6 @@
 
     """Test that the debugger can debug Python."""
 
-    def run_gdb(self, *args, **env_vars):
-        """Runs gdb with the command line given by *args.
-
-        Returns its stdout, stderr
-        """
-        if env_vars:
-            env = os.environ.copy()
-            env.update(env_vars)
-        else:
-            env = None
-        out, err = subprocess.Popen(
-            args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
-            ).communicate()
-        return out, err
-
     def get_stack_trace(self, source=None, script=None,
                         breakpoint='PyObject_Print',
                         cmds_after_breakpoint=None,
@@ -129,7 +140,7 @@
         # print ' '.join(args)
 
         # Use "args" to invoke gdb, capturing stdout, stderr:
-        out, err = self.run_gdb(*args, PYTHONHASHSEED='0')
+        out, err = run_gdb(*args, PYTHONHASHSEED='0')
 
         # Ignore some noise on stderr due to the pending breakpoint:
         err = err.replace('Function "%s" not defined.\n' % breakpoint, '')
@@ -141,6 +152,16 @@
         err = err.replace("warning: Cannot initialize thread debugging"
                           " library: Debugger service failed\n",
                           '')
+        err = err.replace('warning: Could not load shared library symbols for '
+                          'linux-vdso.so.1.\n'
+                          'Do you need "set solib-search-path" or '
+                          '"set sysroot"?\n',
+                          '')
+        err = err.replace('warning: Could not load shared library symbols for '
+                          'linux-gate.so.1.\n'
+                          'Do you need "set solib-search-path" or '
+                          '"set sysroot"?\n',
+                          '')
 
         # Ensure no unexpected error messages:
         self.assertEqual(err, '')
diff --git a/lib-python/2.7/test/test_generators.py b/lib-python/2.7/test/test_generators.py
--- a/lib-python/2.7/test/test_generators.py
+++ b/lib-python/2.7/test/test_generators.py
@@ -383,7 +383,8 @@
 <type 'generator'>
 >>> [s for s in dir(i) if not s.startswith('_')]
 ['close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']
->>> print i.next.__doc__
+>>> from test.test_support import HAVE_DOCSTRINGS
+>>> print(i.next.__doc__ if HAVE_DOCSTRINGS else 'x.next() -> the next value, or raise StopIteration')
 x.next() -> the next value, or raise StopIteration
 >>> iter(i) is i
 True
diff --git a/lib-python/2.7/test/test_genexps.py b/lib-python/2.7/test/test_genexps.py
--- a/lib-python/2.7/test/test_genexps.py
+++ b/lib-python/2.7/test/test_genexps.py
@@ -223,7 +223,8 @@
     >>> set(attr for attr in dir(g) if not attr.startswith('__')) >= expected
     True
 
-    >>> print g.next.__doc__
+    >>> from test.test_support import HAVE_DOCSTRINGS
+    >>> print(g.next.__doc__ if HAVE_DOCSTRINGS else 'x.next() -> the next value, or raise StopIteration')
     x.next() -> the next value, or raise StopIteration
     >>> import types
     >>> isinstance(g, types.GeneratorType)
diff --git a/lib-python/2.7/test/test_glob.py b/lib-python/2.7/test/test_glob.py
--- a/lib-python/2.7/test/test_glob.py
+++ b/lib-python/2.7/test/test_glob.py
@@ -1,8 +1,15 @@
-import unittest
-from test.test_support import run_unittest, TESTFN
 import glob
 import os
 import shutil
+import sys
+import unittest
+
+from test.test_support import run_unittest, TESTFN
+
+
+def fsdecode(s):
+    return unicode(s, sys.getfilesystemencoding())
+
 
 class GlobTests(unittest.TestCase):
 
@@ -18,16 +25,19 @@
         f.close()
 
     def setUp(self):
-        self.tempdir = TESTFN+"_dir"
+        self.tempdir = TESTFN + "_dir"
         self.mktemp('a', 'D')
         self.mktemp('aab', 'F')
+        self.mktemp('.aa', 'G')
+        self.mktemp('.bb', 'H')
         self.mktemp('aaa', 'zzzF')
         self.mktemp('ZZZ')
         self.mktemp('a', 'bcd', 'EF')
         self.mktemp('a', 'bcd', 'efg', 'ha')
         if hasattr(os, 'symlink'):
             os.symlink(self.norm('broken'), self.norm('sym1'))
-            os.symlink(self.norm('broken'), self.norm('sym2'))
+            os.symlink('broken', self.norm('sym2'))
+            os.symlink(os.path.join('a', 'bcd'), self.norm('sym3'))
 
     def tearDown(self):
         shutil.rmtree(self.tempdir)
@@ -40,10 +50,16 @@
         p = os.path.join(self.tempdir, pattern)
         res = glob.glob(p)
         self.assertEqual(list(glob.iglob(p)), res)
+        ures = [fsdecode(x) for x in res]
+        self.assertEqual(glob.glob(fsdecode(p)), ures)
+        self.assertEqual(list(glob.iglob(fsdecode(p))), ures)
         return res
 
     def assertSequencesEqual_noorder(self, l1, l2):
+        l1 = list(l1)
+        l2 = list(l2)
         self.assertEqual(set(l1), set(l2))
+        self.assertEqual(sorted(l1), sorted(l2))
 
     def test_glob_literal(self):
         eq = self.assertSequencesEqual_noorder
@@ -52,20 +68,26 @@
         eq(self.glob('aab'), [self.norm('aab')])
         eq(self.glob('zymurgy'), [])
 
+        res = glob.glob('*')
+        self.assertEqual({type(r) for r in res}, {str})
+        res = glob.glob(os.path.join(os.curdir, '*'))
+        self.assertEqual({type(r) for r in res}, {str})
+
         # test return types are unicode, but only if os.listdir
         # returns unicode filenames
-        uniset = set([unicode])
-        tmp = os.listdir(u'.')
-        if set(type(x) for x in tmp) == uniset:
-            u1 = glob.glob(u'*')
-            u2 = glob.glob(u'./*')
-            self.assertEqual(set(type(r) for r in u1), uniset)
-            self.assertEqual(set(type(r) for r in u2), uniset)
+        tmp = os.listdir(fsdecode(os.curdir))
+        if {type(x) for x in tmp} == {unicode}:
+            res = glob.glob(u'*')
+            self.assertEqual({type(r) for r in res}, {unicode})
+            res = glob.glob(os.path.join(fsdecode(os.curdir), u'*'))
+            self.assertEqual({type(r) for r in res}, {unicode})
 
     def test_glob_one_directory(self):
         eq = self.assertSequencesEqual_noorder
         eq(self.glob('a*'), map(self.norm, ['a', 'aab', 'aaa']))
         eq(self.glob('*a'), map(self.norm, ['a', 'aaa']))
+        eq(self.glob('.*'), map(self.norm, ['.aa', '.bb']))
+        eq(self.glob('?aa'), map(self.norm, ['aaa']))
         eq(self.glob('aa?'), map(self.norm, ['aaa', 'aab']))
         eq(self.glob('aa[ab]'), map(self.norm, ['aaa', 'aab']))
         eq(self.glob('*q'), [])
@@ -87,23 +109,68 @@
         eq(self.glob('*', '*a'), [])
         eq(self.glob('a', '*', '*', '*a'),
            [self.norm('a', 'bcd', 'efg', 'ha')])
-        eq(self.glob('?a?', '*F'), map(self.norm, [os.path.join('aaa', 'zzzF'),
-                                                   os.path.join('aab', 'F')]))
+        eq(self.glob('?a?', '*F'), [self.norm('aaa', 'zzzF'),
+                                    self.norm('aab', 'F')])
 
     def test_glob_directory_with_trailing_slash(self):
-        # We are verifying that when there is wildcard pattern which
-        # ends with os.sep doesn't blow up.
-        res = glob.glob(self.tempdir + '*' + os.sep)
-        self.assertEqual(len(res), 1)
-        # either of these results are reasonable
-        self.assertIn(res[0], [self.tempdir, self.tempdir + os.sep])
+        # Patterns ending with a slash shouldn't match non-dirs
+        res = glob.glob(self.norm('Z*Z') + os.sep)
+        self.assertEqual(res, [])
+        res = glob.glob(self.norm('ZZZ') + os.sep)
+        self.assertEqual(res, [])
+        # When there is a wildcard pattern which ends with os.sep, glob()
+        # doesn't blow up.
+        res = glob.glob(self.norm('aa*') + os.sep)
+        self.assertEqual(len(res), 2)
+        # either of these results is reasonable
+        self.assertIn(set(res), [
+                      {self.norm('aaa'), self.norm('aab')},
+                      {self.norm('aaa') + os.sep, self.norm('aab') + os.sep},
+                      ])
 
+    def test_glob_unicode_directory_with_trailing_slash(self):
+        # Same as test_glob_directory_with_trailing_slash, but with an
+        # unicode argument.
+        res = glob.glob(fsdecode(self.norm('Z*Z') + os.sep))
+        self.assertEqual(res, [])
+        res = glob.glob(fsdecode(self.norm('ZZZ') + os.sep))
+        self.assertEqual(res, [])
+        res = glob.glob(fsdecode(self.norm('aa*') + os.sep))
+        self.assertEqual(len(res), 2)
+        # either of these results is reasonable
+        self.assertIn(set(res), [
+                      {fsdecode(self.norm('aaa')), fsdecode(self.norm('aab'))},
+                      {fsdecode(self.norm('aaa') + os.sep),
+                       fsdecode(self.norm('aab') + os.sep)},
+                      ])
+
+    @unittest.skipUnless(hasattr(os, 'symlink'), "Requires symlink support")
+    def test_glob_symlinks(self):
+        eq = self.assertSequencesEqual_noorder
+        eq(self.glob('sym3'), [self.norm('sym3')])
+        eq(self.glob('sym3', '*'), [self.norm('sym3', 'EF'),
+                                    self.norm('sym3', 'efg')])
+        self.assertIn(self.glob('sym3' + os.sep),
+                      [[self.norm('sym3')], [self.norm('sym3') + os.sep]])
+        eq(self.glob('*', '*F'),
+           [self.norm('aaa', 'zzzF'), self.norm('aab', 'F'),
+            self.norm('sym3', 'EF')])
+
+    @unittest.skipUnless(hasattr(os, 'symlink'), "Requires symlink support")
     def test_glob_broken_symlinks(self):
-        if hasattr(os, 'symlink'):
-            eq = self.assertSequencesEqual_noorder
-            eq(self.glob('sym*'), [self.norm('sym1'), self.norm('sym2')])
-            eq(self.glob('sym1'), [self.norm('sym1')])
-            eq(self.glob('sym2'), [self.norm('sym2')])
+        eq = self.assertSequencesEqual_noorder
+        eq(self.glob('sym*'), [self.norm('sym1'), self.norm('sym2'),
+                               self.norm('sym3')])
+        eq(self.glob('sym1'), [self.norm('sym1')])
+        eq(self.glob('sym2'), [self.norm('sym2')])
+
+    @unittest.skipUnless(sys.platform == "win32", "Win32 specific test")
+    def test_glob_magic_in_drive(self):
+        eq = self.assertSequencesEqual_noorder
+        eq(glob.glob('*:'), [])
+        eq(glob.glob(u'*:'), [])
+        eq(glob.glob('?:'), [])
+        eq(glob.glob(u'?:'), [])
 
 
 def test_main():
diff --git a/lib-python/2.7/test/test_gzip.py b/lib-python/2.7/test/test_gzip.py
--- a/lib-python/2.7/test/test_gzip.py
+++ b/lib-python/2.7/test/test_gzip.py
@@ -53,6 +53,13 @@
             d = f.read()
         self.assertEqual(d, data1*50)
 
+    def test_read_universal_newlines(self):
+        # Issue #5148: Reading breaks when mode contains 'U'.
+        self.test_write()
+        with gzip.GzipFile(self.filename, 'rU') as f:
+            d = f.read()
+        self.assertEqual(d, data1*50)
+
     def test_io_on_closed_object(self):
         # Test that I/O operations on closed GzipFile objects raise a
         # ValueError, just like the corresponding functions on file objects.
@@ -282,6 +289,24 @@
             with gzip.GzipFile(fileobj=f, mode="w") as g:
                 self.assertEqual(g.name, "")
 
+    def test_read_truncated(self):
+        data = data1*50
+        buf = io.BytesIO()
+        with gzip.GzipFile(fileobj=buf, mode="w") as f:
+            f.write(data)
+        # Drop the CRC (4 bytes) and file size (4 bytes).
+        truncated = buf.getvalue()[:-8]
+        with gzip.GzipFile(fileobj=io.BytesIO(truncated)) as f:
+            self.assertRaises(EOFError, f.read)
+        with gzip.GzipFile(fileobj=io.BytesIO(truncated)) as f:
+            self.assertEqual(f.read(len(data)), data)
+            self.assertRaises(EOFError, f.read, 1)
+        # Incomplete 10-byte header.
+        for i in range(2, 10):
+            with gzip.GzipFile(fileobj=io.BytesIO(truncated[:i])) as f:
+                self.assertRaises(EOFError, f.read, 1)
+
+
 def test_main(verbose=None):
     test_support.run_unittest(TestGzip)
 
diff --git a/lib-python/2.7/test/test_hashlib.py b/lib-python/2.7/test/test_hashlib.py
--- a/lib-python/2.7/test/test_hashlib.py
+++ b/lib-python/2.7/test/test_hashlib.py
@@ -108,12 +108,8 @@
                                                 _algo.islower()]))
 
     def test_unknown_hash(self):
-        try:
-            hashlib.new('spam spam spam spam spam')
-        except ValueError:
-            pass
-        else:
-            self.assertTrue(0 == "hashlib didn't reject bogus hash name")
+        self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
+        self.assertRaises(TypeError, hashlib.new, 1)
 
     def test_get_builtin_constructor(self):
         get_builtin_constructor = hashlib.__dict__[
@@ -132,6 +128,7 @@
                 sys.modules['_md5'] = _md5
             else:
                 del sys.modules['_md5']
+        self.assertRaises(TypeError, get_builtin_constructor, 3)
 
     def test_hexdigest(self):
         for name in self.supported_hash_names:
@@ -170,6 +167,21 @@
                     % (name, hash_object_constructor,
                        computed, len(data), digest))
 
+    def check_update(self, name, data, digest):
+        constructors = self.constructors_to_test[name]
+        # 2 is for hashlib.name(...) and hashlib.new(name, ...)
+        self.assertGreaterEqual(len(constructors), 2)
+        for hash_object_constructor in constructors:
+            h = hash_object_constructor()
+            h.update(data)
+            computed = h.hexdigest()
+            self.assertEqual(
+                    computed, digest,
+                    "Hash algorithm %s using %s when updated returned hexdigest"
+                    " %r for %d byte input data that should have hashed to %r."
+                    % (name, hash_object_constructor,
+                       computed, len(data), digest))
+
     def check_unicode(self, algorithm_name):
         # Unicode objects are not allowed as input.
         expected = hashlib.new(algorithm_name, str(u'spam')).hexdigest()
@@ -203,6 +215,15 @@
             except OverflowError:
                 pass # 32-bit arch
 
+    @precisionbigmemtest(size=_4G + 5, memuse=1)
+    def test_case_md5_huge_update(self, size):
+        if size == _4G + 5:
+            try:
+                self.check_update('md5', 'A'*size,
+                        'c9af2dff37468ce5dfee8f2cfc0a9c6d')
+            except OverflowError:
+                pass # 32-bit arch
+
     @precisionbigmemtest(size=_4G - 1, memuse=1)
     def test_case_md5_uintmax(self, size):
         if size == _4G - 1:
@@ -231,6 +252,23 @@
         self.check('sha1', "a" * 1000000,
                    "34aa973cd4c4daa4f61eeb2bdbad27316534016f")
 
+    @precisionbigmemtest(size=_4G + 5, memuse=1)
+    def test_case_sha1_huge(self, size):
+        if size == _4G + 5:
+            try:
+                self.check('sha1', 'A'*size,
+                        '87d745c50e6b2879ffa0fb2c930e9fbfe0dc9a5b')
+            except OverflowError:
+                pass # 32-bit arch
+
+    @precisionbigmemtest(size=_4G + 5, memuse=1)
+    def test_case_sha1_huge_update(self, size):
+        if size == _4G + 5:
+            try:
+                self.check_update('sha1', 'A'*size,
+                        '87d745c50e6b2879ffa0fb2c930e9fbfe0dc9a5b')
+            except OverflowError:
+                pass # 32-bit arch
 
     # use the examples from Federal Information Processing Standards
     # Publication 180-2, Secure Hash Standard,  2002 August 1
diff --git a/lib-python/2.7/test/test_heapq.py b/lib-python/2.7/test/test_heapq.py
--- a/lib-python/2.7/test/test_heapq.py
+++ b/lib-python/2.7/test/test_heapq.py
@@ -315,6 +315,16 @@
     'Test multiple tiers of iterators'
     return chain(imap(lambda x:x, R(Ig(G(seqn)))))
 
+class SideEffectLT:
+    def __init__(self, value, heap):
+        self.value = value
+        self.heap = heap
+
+    def __lt__(self, other):
+        self.heap[:] = []
+        return self.value < other.value
+
+
 class TestErrorHandling(TestCase):
     module = None
 
@@ -361,6 +371,22 @@
                 self.assertRaises(TypeError, f, 2, N(s))
                 self.assertRaises(ZeroDivisionError, f, 2, E(s))
 
+    # Issue #17278: the heap may change size while it's being walked.
+
+    def test_heappush_mutating_heap(self):
+        heap = []
+        heap.extend(SideEffectLT(i, heap) for i in range(200))
+        # Python version raises IndexError, C version RuntimeError
+        with self.assertRaises((IndexError, RuntimeError)):
+            self.module.heappush(heap, SideEffectLT(5, heap))
+
+    def test_heappop_mutating_heap(self):
+        heap = []
+        heap.extend(SideEffectLT(i, heap) for i in range(200))
+        # Python version raises IndexError, C version RuntimeError
+        with self.assertRaises((IndexError, RuntimeError)):
+            self.module.heappop(heap)
+
 
 class TestErrorHandlingPython(TestErrorHandling):
     module = py_heapq
diff --git a/lib-python/2.7/test/test_htmlparser.py b/lib-python/2.7/test/test_htmlparser.py
--- a/lib-python/2.7/test/test_htmlparser.py
+++ b/lib-python/2.7/test/test_htmlparser.py
@@ -260,6 +260,16 @@
             ('starttag', 'a', [('foo', None), ('=', None), ('bar', None)])
         ]
         self._run_check(html, expected)
+        #see issue #14538
+        html = ('<meta><meta / ><meta // ><meta / / >'
+                '<meta/><meta /><meta //><meta//>')
+        expected = [
+            ('starttag', 'meta', []), ('starttag', 'meta', []),
+            ('starttag', 'meta', []), ('starttag', 'meta', []),
+            ('startendtag', 'meta', []), ('startendtag', 'meta', []),
+            ('startendtag', 'meta', []), ('startendtag', 'meta', []),
+        ]
+        self._run_check(html, expected)
 
     def test_declaration_junk_chars(self):
         self._run_check("<!DOCTYPE foo $ >", [('decl', 'DOCTYPE foo $ ')])
diff --git a/lib-python/2.7/test/test_httplib.py b/lib-python/2.7/test/test_httplib.py
--- a/lib-python/2.7/test/test_httplib.py
+++ b/lib-python/2.7/test/test_httplib.py
@@ -90,6 +90,34 @@
                 conn.request('POST', '/', body, headers)
                 self.assertEqual(conn._buffer.count[header.lower()], 1)
 
+    def test_content_length_0(self):
+
+        class ContentLengthChecker(list):
+            def __init__(self):
+                list.__init__(self)
+                self.content_length = None
+            def append(self, item):
+                kv = item.split(':', 1)
+                if len(kv) > 1 and kv[0].lower() == 'content-length':
+                    self.content_length = kv[1].strip()
+                list.append(self, item)
+
+        # POST with empty body
+        conn = httplib.HTTPConnection('example.com')
+        conn.sock = FakeSocket(None)
+        conn._buffer = ContentLengthChecker()
+        conn.request('POST', '/', '')
+        self.assertEqual(conn._buffer.content_length, '0',
+                        'Header Content-Length not set')
+
+        # PUT request with empty body
+        conn = httplib.HTTPConnection('example.com')
+        conn.sock = FakeSocket(None)
+        conn._buffer = ContentLengthChecker()
+        conn.request('PUT', '/', '')
+        self.assertEqual(conn._buffer.content_length, '0',
+                        'Header Content-Length not set')
+
     def test_putheader(self):
         conn = httplib.HTTPConnection('example.com')
         conn.sock = FakeSocket(None)
@@ -138,7 +166,7 @@
         self.assertEqual(repr(exc), '''BadStatusLine("\'\'",)''')
 
     def test_partial_reads(self):
-        # if we have a lenght, the system knows when to close itself
+        # if we have a length, the system knows when to close itself
         # same behaviour than when we read the whole thing with read()
         body = "HTTP/1.1 200 Ok\r\nContent-Length: 4\r\n\r\nText"
         sock = FakeSocket(body)
@@ -149,6 +177,32 @@
         self.assertEqual(resp.read(2), 'xt')
         self.assertTrue(resp.isclosed())
 
+    def test_partial_reads_no_content_length(self):
+        # when no length is present, the socket should be gracefully closed when
+        # all data was read
+        body = "HTTP/1.1 200 Ok\r\n\r\nText"
+        sock = FakeSocket(body)
+        resp = httplib.HTTPResponse(sock)
+        resp.begin()
+        self.assertEqual(resp.read(2), 'Te')
+        self.assertFalse(resp.isclosed())
+        self.assertEqual(resp.read(2), 'xt')
+        self.assertEqual(resp.read(1), '')
+        self.assertTrue(resp.isclosed())
+
+    def test_partial_reads_incomplete_body(self):
+        # if the server shuts down the connection before the whole
+        # content-length is delivered, the socket is gracefully closed
+        body = "HTTP/1.1 200 Ok\r\nContent-Length: 10\r\n\r\nText"
+        sock = FakeSocket(body)
+        resp = httplib.HTTPResponse(sock)
+        resp.begin()
+        self.assertEqual(resp.read(2), 'Te')
+        self.assertFalse(resp.isclosed())
+        self.assertEqual(resp.read(2), 'xt')
+        self.assertEqual(resp.read(1), '')
+        self.assertTrue(resp.isclosed())
+
     def test_host_port(self):
         # Check invalid host_port
 
@@ -279,7 +333,7 @@
         resp = httplib.HTTPResponse(sock, method="GET")
         resp.begin()
         self.assertEqual(resp.read(), 'Hello\r\n')
-        resp.close()
+        self.assertTrue(resp.isclosed())
 
     def test_incomplete_read(self):
         sock = FakeSocket('HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nHello\r\n')
@@ -293,10 +347,9 @@
                              "IncompleteRead(7 bytes read, 3 more expected)")
             self.assertEqual(str(i),
                              "IncompleteRead(7 bytes read, 3 more expected)")
+            self.assertTrue(resp.isclosed())
         else:
             self.fail('IncompleteRead expected')
-        finally:
-            resp.close()
 
     def test_epipe(self):
         sock = EPipeSocket(
@@ -349,6 +402,14 @@
         resp.begin()
         self.assertRaises(httplib.LineTooLong, resp.read)
 
+    def test_early_eof(self):
+        # Test httpresponse with no \r\n termination,
+        body = "HTTP/1.1 200 Ok"
+        sock = FakeSocket(body)
+        resp = httplib.HTTPResponse(sock)
+        resp.begin()
+        self.assertEqual(resp.read(), '')
+        self.assertTrue(resp.isclosed())
 
 class OfflineTest(TestCase):
     def test_responses(self):
diff --git a/lib-python/2.7/test/test_httpservers.py b/lib-python/2.7/test/test_httpservers.py
--- a/lib-python/2.7/test/test_httpservers.py
+++ b/lib-python/2.7/test/test_httpservers.py
@@ -4,11 +4,6 @@
 Josip Dzolonga, and Michael Otteneder for the 2007/08 GHOP contest.
 """
 
-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
-from SimpleHTTPServer import SimpleHTTPRequestHandler
-from CGIHTTPServer import CGIHTTPRequestHandler
-import CGIHTTPServer
-
 import os
 import sys
 import re
@@ -17,12 +12,17 @@
 import urllib
 import httplib
 import tempfile
+import unittest
+import CGIHTTPServer
 
-import unittest
 
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+from SimpleHTTPServer import SimpleHTTPRequestHandler
+from CGIHTTPServer import CGIHTTPRequestHandler
 from StringIO import StringIO
+from test import test_support
 
-from test import test_support
+
 threading = test_support.import_module('threading')
 
 
@@ -43,7 +43,7 @@
         self.end_headers()
         self.wfile.write(b'<html><body>Data</body></html>\r\n')
 
-    def log_message(self, format, *args):
+    def log_message(self, fmt, *args):
         pass
 
 
@@ -97,9 +97,9 @@
         self.handler = SocketlessRequestHandler()
 
     def send_typical_request(self, message):
-        input = StringIO(message)
+        input_msg = StringIO(message)
         output = StringIO()
-        self.handler.rfile = input
+        self.handler.rfile = input_msg
         self.handler.wfile = output
         self.handler.handle_one_request()
         output.seek(0)
@@ -296,7 +296,7 @@
             os.chdir(self.cwd)
             try:
                 shutil.rmtree(self.tempdir)
-            except:
+            except OSError:
                 pass
         finally:
             BaseTestCase.tearDown(self)
@@ -418,42 +418,44 @@
         finally:
             BaseTestCase.tearDown(self)
 
-    def test_url_collapse_path_split(self):
+    def test_url_collapse_path(self):
+        # verify tail is the last portion and head is the rest on proper urls
         test_vectors = {
-            '': ('/', ''),
+            '': '//',
             '..': IndexError,
             '/.//..': IndexError,
-            '/': ('/', ''),
-            '//': ('/', ''),
-            '/\\': ('/', '\\'),
-            '/.//': ('/', ''),
-            'cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
-            '/cgi-bin/file1.py': ('/cgi-bin', 'file1.py'),
-            '/cgi-bin/file1.py/PATH-INFO': ('/cgi-bin', 'file1.py/PATH-INFO'),
-            'a': ('/', 'a'),
-            '/a': ('/', 'a'),
-            '//a': ('/', 'a'),
-            './a': ('/', 'a'),
-            './C:/': ('/C:', ''),
-            '/a/b': ('/a', 'b'),
-            '/a/b/': ('/a/b', ''),
-            '/a/b/c/..': ('/a/b', ''),
-            '/a/b/c/../d': ('/a/b', 'd'),
-            '/a/b/c/../d/e/../f': ('/a/b/d', 'f'),
-            '/a/b/c/../d/e/../../f': ('/a/b', 'f'),
-            '/a/b/c/../d/e/.././././..//f': ('/a/b', 'f'),
+            '/': '//',
+            '//': '//',
+            '/\\': '//\\',
+            '/.//': '//',
+            'cgi-bin/file1.py': '/cgi-bin/file1.py',
+            '/cgi-bin/file1.py': '/cgi-bin/file1.py',
+            'a': '//a',
+            '/a': '//a',
+            '//a': '//a',
+            './a': '//a',
+            './C:/': '/C:/',
+            '/a/b': '/a/b',
+            '/a/b/': '/a/b/',
+            '/a/b/.': '/a/b/',
+            '/a/b/c/..': '/a/b/',
+            '/a/b/c/../d': '/a/b/d',
+            '/a/b/c/../d/e/../f': '/a/b/d/f',
+            '/a/b/c/../d/e/../../f': '/a/b/f',
+            '/a/b/c/../d/e/.././././..//f': '/a/b/f',
             '../a/b/c/../d/e/.././././..//f': IndexError,
-            '/a/b/c/../d/e/../../../f': ('/a', 'f'),
-            '/a/b/c/../d/e/../../../../f': ('/', 'f'),
+            '/a/b/c/../d/e/../../../f': '/a/f',
+            '/a/b/c/../d/e/../../../../f': '//f',
             '/a/b/c/../d/e/../../../../../f': IndexError,
-            '/a/b/c/../d/e/../../../../f/..': ('/', ''),
+            '/a/b/c/../d/e/../../../../f/..': '//',
+            '/a/b/c/../d/e/../../../../f/../.': '//',
         }
         for path, expected in test_vectors.iteritems():
             if isinstance(expected, type) and issubclass(expected, Exception):
                 self.assertRaises(expected,
-                                  CGIHTTPServer._url_collapse_path_split, path)
+                                  CGIHTTPServer._url_collapse_path, path)
             else:
-                actual = CGIHTTPServer._url_collapse_path_split(path)
+                actual = CGIHTTPServer._url_collapse_path(path)
                 self.assertEqual(expected, actual,
                                  msg='path = %r\nGot:    %r\nWanted: %r' %
                                  (path, actual, expected))
diff --git a/lib-python/2.7/test/test_imaplib.py b/lib-python/2.7/test/test_imaplib.py
--- a/lib-python/2.7/test/test_imaplib.py
+++ b/lib-python/2.7/test/test_imaplib.py
@@ -79,7 +79,7 @@
                         return
                     line += part
                 except IOError:
-                    # ..but SSLSockets throw exceptions.
+                    # ..but SSLSockets raise exceptions.
                     return
                 if line.endswith('\r\n'):
                     break
diff --git a/lib-python/2.7/test/test_import.py b/lib-python/2.7/test/test_import.py
--- a/lib-python/2.7/test/test_import.py
+++ b/lib-python/2.7/test/test_import.py
@@ -5,6 +5,7 @@
 import py_compile
 import random
 import stat
+import struct
 import sys
 import unittest
 import textwrap
@@ -15,12 +16,23 @@
 from test import symlink_support
 from test import script_helper
 
+def _files(name):
+    return (name + os.extsep + "py",
+            name + os.extsep + "pyc",
+            name + os.extsep + "pyo",
+            name + os.extsep + "pyw",
+            name + "$py.class")
+
+def chmod_files(name):
+    for f in _files(name):
+        try:
+            os.chmod(f, 0600)
+        except OSError as exc:
+            if exc.errno != errno.ENOENT:
+                raise
+
 def remove_files(name):
-    for f in (name + os.extsep + "py",
-              name + os.extsep + "pyc",
-              name + os.extsep + "pyo",
-              name + os.extsep + "pyw",
-              name + "$py.class"):
+    for f in _files(name):
         unlink(f)
 
 
@@ -120,6 +132,40 @@
             unload(TESTFN)
             del sys.path[0]
 
+    def test_rewrite_pyc_with_read_only_source(self):
+        # Issue 6074: a long time ago on posix, and more recently on Windows,
+        # a read only source file resulted in a read only pyc file, which
+        # led to problems with updating it later
+        sys.path.insert(0, os.curdir)
+        fname = TESTFN + os.extsep + "py"
+        try:
+            # Write a Python file, make it read-only and import it
+            with open(fname, 'w') as f:
+                f.write("x = 'original'\n")
+            # Tweak the mtime of the source to ensure pyc gets updated later
+            s = os.stat(fname)
+            os.utime(fname, (s.st_atime, s.st_mtime-100000000))
+            os.chmod(fname, 0400)
+            m1 = __import__(TESTFN)
+            self.assertEqual(m1.x, 'original')
+            # Change the file and then reimport it
+            os.chmod(fname, 0600)
+            with open(fname, 'w') as f:
+                f.write("x = 'rewritten'\n")
+            unload(TESTFN)
+            m2 = __import__(TESTFN)
+            self.assertEqual(m2.x, 'rewritten')
+            # Now delete the source file and check the pyc was rewritten
+            unlink(fname)
+            unload(TESTFN)
+            m3 = __import__(TESTFN)
+            self.assertEqual(m3.x, 'rewritten')
+        finally:
+            chmod_files(TESTFN)
+            remove_files(TESTFN)
+            unload(TESTFN)
+            del sys.path[0]
+
     def test_imp_module(self):
         # Verify that the imp module can correctly load and find .py files
 
@@ -305,6 +351,46 @@
             del sys.path[0]
             remove_files(TESTFN)
 
+    def test_pyc_mtime(self):
+        # Test for issue #13863: .pyc timestamp sometimes incorrect on Windows.
+        sys.path.insert(0, os.curdir)
+        try:
+            # Jan 1, 2012; Jul 1, 2012.
+            mtimes = 1325376000, 1341100800
+
+            # Different names to avoid running into import caching.
+            tails = "spam", "eggs"
+            for mtime, tail in zip(mtimes, tails):
+                module = TESTFN + tail
+                source = module + ".py"
+                compiled = source + ('c' if __debug__ else 'o')
+
+                # Create a new Python file with the given mtime.
+                with open(source, 'w') as f:
+                    f.write("# Just testing\nx=1, 2, 3\n")
+                os.utime(source, (mtime, mtime))
+
+                # Generate the .pyc/o file; if it couldn't be created
+                # for some reason, skip the test.
+                m = __import__(module)
+                if not os.path.exists(compiled):
+                    unlink(source)
+                    self.skipTest("Couldn't create .pyc/.pyo file.")
+
+                # Actual modification time of .py file.
+                mtime1 = int(os.stat(source).st_mtime) & 0xffffffff
+
+                # mtime that was encoded in the .pyc file.
+                with open(compiled, 'rb') as f:
+                    mtime2 = struct.unpack('<L', f.read(8)[4:])[0]
+
+                unlink(compiled)
+                unlink(source)
+
+                self.assertEqual(mtime1, mtime2)
+        finally:
+            sys.path.pop(0)
+
 
 class PycRewritingTests(unittest.TestCase):
     # Test that the `co_filename` attribute on code objects always points
@@ -427,6 +513,13 @@
         drive = path[0]
         unc = "\\\\%s\\%s$"%(hn, drive)
         unc += path[2:]
+        try:
+            os.listdir(unc)
+        except OSError as e:
+            if e.errno in (errno.EPERM, errno.EACCES):
+                # See issue #15338
+                self.skipTest("cannot access administrative share %r" % (unc,))
+            raise
         sys.path.append(path)
         mod = __import__("test_trailing_slash")
         self.assertEqual(mod.testdata, 'test_trailing_slash')
diff --git a/lib-python/2.7/test/test_int.py b/lib-python/2.7/test/test_int.py
--- a/lib-python/2.7/test/test_int.py
+++ b/lib-python/2.7/test/test_int.py
@@ -1,6 +1,7 @@
 import sys
 
 import unittest
+from test import test_support
 from test.test_support import run_unittest, have_unicode
 import math
 
@@ -44,7 +45,27 @@
         (unichr(0x200), ValueError),
 ]
 
-class IntTestCases(unittest.TestCase):
+class IntLongCommonTests(object):
+
+    """Mixin of test cases to share between both test_int and test_long."""
+
+    # Change to int or long in the TestCase subclass.
+    ntype = None
+
+    def test_no_args(self):
+        self.assertEqual(self.ntype(), 0)
+
+    def test_keyword_args(self):
+        # Test invoking constructor using keyword arguments.
+        self.assertEqual(self.ntype(x=1.2), 1)
+        self.assertEqual(self.ntype('100', base=2), 4)
+        self.assertEqual(self.ntype(x='100', base=2), 4)
+        self.assertRaises(TypeError, self.ntype, base=10)
+        self.assertRaises(TypeError, self.ntype, base=0)
+
+class IntTestCases(IntLongCommonTests, unittest.TestCase):
+
+    ntype = int
 
     def test_basic(self):
         self.assertEqual(int(314), 314)
@@ -315,6 +336,46 @@
         self.assertEqual(int(float(2**54+10)), 2**54+8)
         self.assertEqual(int(float(2**54+11)), 2**54+12)
 
+    def test_valid_non_numeric_input_types_for_x(self):
+        # Test possible valid non-numeric types for x, including subclasses
+        # of the allowed built-in types.
+        class CustomStr(str): pass
+        values = ['100', CustomStr('100')]
+
+        if have_unicode:
+            class CustomUnicode(unicode): pass
+            values += [unicode('100'), CustomUnicode(unicode('100'))]
+
+        for x in values:
+            msg = 'x has value %s and type %s' % (x, type(x).__name__)
+            try:
+                self.assertEqual(int(x), 100, msg=msg)
+                self.assertEqual(int(x, 2), 4, msg=msg)
+            except TypeError, err:
+                raise AssertionError('For %s got TypeError: %s' %
+                                     (type(x).__name__, err))
+
+    def test_error_on_string_float_for_x(self):
+        self.assertRaises(ValueError, int, '1.2')
+
+    def test_error_on_bytearray_for_x(self):
+        self.assertRaises(TypeError, int, bytearray('100'), 2)
+
+    def test_error_on_invalid_int_bases(self):
+        for base in [-1, 1, 1000]:
+            self.assertRaises(ValueError, int, '100', base)
+
+    def test_error_on_string_base(self):
+        self.assertRaises(TypeError, int, 100, base='foo')
+
+    @test_support.cpython_only
+    def test_small_ints(self):
+        self.assertIs(int('10'), 10)
+        self.assertIs(int('-1'), -1)
+        if have_unicode:
+            self.assertIs(int(u'10'), 10)
+            self.assertIs(int(u'-1'), -1)
+
     def test_intconversion(self):
         # Test __int__()
         class ClassicMissingMethods:
diff --git a/lib-python/2.7/test/test_io.py b/lib-python/2.7/test/test_io.py
--- a/lib-python/2.7/test/test_io.py
+++ b/lib-python/2.7/test/test_io.py
@@ -34,7 +34,9 @@
 import errno
 from itertools import cycle, count
 from collections import deque
+from UserList import UserList
 from test import test_support as support
+import contextlib
 
 import codecs
 import io  # C implementation of io
@@ -572,6 +574,7 @@
             raise IOError()
         f.flush = bad_flush
         self.assertRaises(IOError, f.close) # exception not swallowed
+        self.assertTrue(f.closed)
 
     def test_multi_close(self):
         f = self.open(support.TESTFN, "wb", buffering=0)
@@ -593,6 +596,19 @@
         self.assertEqual(rawio.read(2), None)
         self.assertEqual(rawio.read(2), b"")
 
+    def test_fileio_closefd(self):
+        # Issue #4841
+        with self.open(__file__, 'rb') as f1, \
+             self.open(__file__, 'rb') as f2:
+            fileio = self.FileIO(f1.fileno(), closefd=False)
+            # .__init__() must not close f1
+            fileio.__init__(f2.fileno(), closefd=False)
+            f1.readline()
+            # .close() must not close f2
+            fileio.close()
+            f2.readline()
+
+
 class CIOTest(IOTest):
 
     def test_IOBase_finalize(self):
@@ -718,6 +734,21 @@
         raw.flush = bad_flush
         b = self.tp(raw)
         self.assertRaises(IOError, b.close) # exception not swallowed
+        self.assertTrue(b.closed)
+
+    def test_close_error_on_close(self):
+        raw = self.MockRawIO()
+        def bad_flush():
+            raise IOError('flush')
+        def bad_close():
+            raise IOError('close')
+        raw.close = bad_close
+        b = self.tp(raw)
+        b.flush = bad_flush
+        with self.assertRaises(IOError) as err: # exception not swallowed
+            b.close()
+        self.assertEqual(err.exception.args, ('close',))
+        self.assertFalse(b.closed)
 
     def test_multi_close(self):
         raw = self.MockRawIO()
@@ -735,6 +766,20 @@
             buf.raw = x
 
 
+class SizeofTest:
+
+    @support.cpython_only
+    def test_sizeof(self):
+        bufsize1 = 4096
+        bufsize2 = 8192
+        rawio = self.MockRawIO()
+        bufio = self.tp(rawio, buffer_size=bufsize1)
+        size = sys.getsizeof(bufio) - bufsize1
+        rawio = self.MockRawIO()
+        bufio = self.tp(rawio, buffer_size=bufsize2)
+        self.assertEqual(sys.getsizeof(bufio), size + bufsize2)
+
+
 class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
     read_mode = "rb"
 
@@ -918,7 +963,7 @@
                              "failed for {}: {} != 0".format(n, rawio._extraneous_reads))
 
 
-class CBufferedReaderTest(BufferedReaderTest):
+class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
     tp = io.BufferedReader
 
     def test_constructor(self):
@@ -959,6 +1004,12 @@
         support.gc_collect()
         self.assertTrue(wr() is None, wr)
 
+    def test_args_error(self):
+        # Issue #17275
+        with self.assertRaisesRegexp(TypeError, "BufferedReader"):
+            self.tp(io.BytesIO(), 1024, 1024, 1024)
+
+
 class PyBufferedReaderTest(BufferedReaderTest):
     tp = pyio.BufferedReader
 
@@ -1099,6 +1150,28 @@
         bufio.flush()
         self.assertEqual(b"abc", writer._write_stack[0])
 
+    def test_writelines(self):
+        l = [b'ab', b'cd', b'ef']
+        writer = self.MockRawIO()
+        bufio = self.tp(writer, 8)
+        bufio.writelines(l)
+        bufio.flush()
+        self.assertEqual(b''.join(writer._write_stack), b'abcdef')
+
+    def test_writelines_userlist(self):
+        l = UserList([b'ab', b'cd', b'ef'])
+        writer = self.MockRawIO()
+        bufio = self.tp(writer, 8)
+        bufio.writelines(l)
+        bufio.flush()
+        self.assertEqual(b''.join(writer._write_stack), b'abcdef')
+
+    def test_writelines_error(self):
+        writer = self.MockRawIO()
+        bufio = self.tp(writer, 8)
+        self.assertRaises(TypeError, bufio.writelines, [1, 2, 3])
+        self.assertRaises(TypeError, bufio.writelines, None)
+
     def test_destructor(self):
         writer = self.MockRawIO()
         bufio = self.tp(writer, 8)
@@ -1180,8 +1253,18 @@
                                      DeprecationWarning)):
             self.tp(self.MockRawIO(), 8, 12)
 
-
-class CBufferedWriterTest(BufferedWriterTest):
+    def test_write_error_on_close(self):
+        raw = self.MockRawIO()
+        def bad_write(b):
+            raise IOError()
+        raw.write = bad_write
+        b = self.tp(raw)
+        b.write(b'spam')
+        self.assertRaises(IOError, b.close) # exception not swallowed
+        self.assertTrue(b.closed)
+
+
+class CBufferedWriterTest(BufferedWriterTest, SizeofTest):
     tp = io.BufferedWriter
 
     def test_constructor(self):
@@ -1219,6 +1302,11 @@
         with self.open(support.TESTFN, "rb") as f:
             self.assertEqual(f.read(), b"123xxx")
 
+    def test_args_error(self):
+        # Issue #17275
+        with self.assertRaisesRegexp(TypeError, "BufferedWriter"):
+            self.tp(io.BytesIO(), 1024, 1024, 1024)
+
 
 class PyBufferedWriterTest(BufferedWriterTest):
     tp = pyio.BufferedWriter
@@ -1570,7 +1658,8 @@
                 self.assertEqual(raw.getvalue(), b'1b\n2def\n3\n')
 
 
-class CBufferedRandomTest(CBufferedReaderTest, CBufferedWriterTest, BufferedRandomTest):
+class CBufferedRandomTest(CBufferedReaderTest, CBufferedWriterTest,
+                          BufferedRandomTest, SizeofTest):
     tp = io.BufferedRandom
 
     def test_constructor(self):
@@ -1587,6 +1676,12 @@
         CBufferedReaderTest.test_garbage_collection(self)
         CBufferedWriterTest.test_garbage_collection(self)
 
+    def test_args_error(self):
+        # Issue #17275
+        with self.assertRaisesRegexp(TypeError, "BufferedRandom"):
+            self.tp(io.BytesIO(), 1024, 1024, 1024)
+
+
 class PyBufferedRandomTest(BufferedRandomTest):
     tp = pyio.BufferedRandom
 
@@ -2183,6 +2278,28 @@
             reads += c
         self.assertEqual(reads, "A"*127+"\nB")
 
+    def test_writelines(self):
+        l = ['ab', 'cd', 'ef']
+        buf = self.BytesIO()
+        txt = self.TextIOWrapper(buf)
+        txt.writelines(l)
+        txt.flush()
+        self.assertEqual(buf.getvalue(), b'abcdef')
+
+    def test_writelines_userlist(self):
+        l = UserList(['ab', 'cd', 'ef'])
+        buf = self.BytesIO()
+        txt = self.TextIOWrapper(buf)
+        txt.writelines(l)
+        txt.flush()
+        self.assertEqual(buf.getvalue(), b'abcdef')
+
+    def test_writelines_error(self):
+        txt = self.TextIOWrapper(self.BytesIO())
+        self.assertRaises(TypeError, txt.writelines, [1, 2, 3])
+        self.assertRaises(TypeError, txt.writelines, None)
+        self.assertRaises(TypeError, txt.writelines, b'abc')
+
     def test_issue1395_1(self):
         txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
 
@@ -2306,6 +2423,7 @@
             raise IOError()
         txt.flush = bad_flush
         self.assertRaises(IOError, txt.close) # exception not swallowed
+        self.assertTrue(txt.closed)
 
     def test_multi_close(self):
         txt = self.TextIOWrapper(self.BytesIO(self.testdata), encoding="ascii")
@@ -2320,6 +2438,39 @@
         with self.assertRaises((AttributeError, TypeError)):
             txt.buffer = buf
 
+    def test_read_nonbytes(self):
+        # Issue #17106
+        # Crash when underlying read() returns non-bytes
+        class NonbytesStream(self.StringIO):
+            read1 = self.StringIO.read
+        class NonbytesStream(self.StringIO):
+            read1 = self.StringIO.read
+        t = self.TextIOWrapper(NonbytesStream('a'))
+        with self.maybeRaises(TypeError):
+            t.read(1)
+        t = self.TextIOWrapper(NonbytesStream('a'))
+        with self.maybeRaises(TypeError):
+            t.readline()
+        t = self.TextIOWrapper(NonbytesStream('a'))
+        self.assertEqual(t.read(), u'a')
+
+    def test_illegal_decoder(self):
+        # Issue #17106
+        # Crash when decoder returns non-string
+        t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n',
+                               encoding='quopri_codec')
+        with self.maybeRaises(TypeError):
+            t.read(1)
+        t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n',
+                               encoding='quopri_codec')
+        with self.maybeRaises(TypeError):
+            t.readline()
+        t = self.TextIOWrapper(self.BytesIO(b'aaaaaa'), newline='\n',
+                               encoding='quopri_codec')
+        with self.maybeRaises(TypeError):
+            t.read()
+
+
 class CTextIOWrapperTest(TextIOWrapperTest):
 
     def test_initialization(self):
@@ -2361,9 +2512,13 @@
             t2.buddy = t1
         support.gc_collect()
 
+    maybeRaises = unittest.TestCase.assertRaises
+
 
 class PyTextIOWrapperTest(TextIOWrapperTest):
-    pass
+    @contextlib.contextmanager
+    def maybeRaises(self, *args, **kwds):
+        yield
 
 
 class IncrementalNewlineDecoderTest(unittest.TestCase):
diff --git a/lib-python/2.7/test/test_iter.py b/lib-python/2.7/test/test_iter.py
--- a/lib-python/2.7/test/test_iter.py
+++ b/lib-python/2.7/test/test_iter.py
@@ -908,6 +908,21 @@
         except TypeError:
             pass
 
+    def test_extending_list_with_iterator_does_not_segfault(self):
+        # The code to extend a list with an iterator has a fair
+        # amount of nontrivial logic in terms of guessing how
+        # much memory to allocate in advance, "stealing" refs,
+        # and then shrinking at the end.  This is a basic smoke
+        # test for that scenario.
+        def gen():
+            for i in range(500):
+                yield i
+        lst = [0] * 500
+        for i in range(240):
+            lst.pop(0)
+        lst.extend(gen())
+        self.assertEqual(len(lst), 760)
+
 
 def test_main():
     run_unittest(TestCase)
diff --git a/lib-python/2.7/test/test_itertools.py b/lib-python/2.7/test/test_itertools.py
--- a/lib-python/2.7/test/test_itertools.py
+++ b/lib-python/2.7/test/test_itertools.py
@@ -906,6 +906,12 @@
         del a
         self.assertRaises(ReferenceError, getattr, p, '__class__')
 
+    # Issue 13454: Crash when deleting backward iterator from tee()
+    def test_tee_del_backward(self):
+        forward, backward = tee(repeat(None, 20000000))
+        any(forward)  # exhaust the iterator
+        del backward
+
     def test_StopIteration(self):
         self.assertRaises(StopIteration, izip().next)
 
diff --git a/lib-python/2.7/test/test_logging.py b/lib-python/2.7/test/test_logging.py
--- a/lib-python/2.7/test/test_logging.py
+++ b/lib-python/2.7/test/test_logging.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2001-2010 by Vinay Sajip. All Rights Reserved.
+# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved.
 #
 # Permission to use, copy, modify, and distribute this software and its
 # documentation for any purpose and without fee is hereby granted,
@@ -18,7 +18,7 @@
 
 """Test harness for the logging module. Run all tests.
 
-Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved.
+Copyright (C) 2001-2012 Vinay Sajip. All Rights Reserved.
 """
 
 import logging
@@ -31,6 +31,7 @@
 import gc
 import json
 import os
+import random
 import re
 import select
 import socket
@@ -40,6 +41,7 @@
 import tempfile
 from test.test_support import captured_stdout, run_with_locale, run_unittest
 import textwrap
+import time
 import unittest
 import warnings
 import weakref
@@ -1873,6 +1875,47 @@
         self.assertTrue(c2 is c3)
 
 
+class HandlerTest(BaseTest):
+
+    @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
+    @unittest.skipUnless(threading, 'Threading required for this test.')
+    def test_race(self):
+        # Issue #14632 refers.
+        def remove_loop(fname, tries):
+            for _ in range(tries):
+                try:
+                    os.unlink(fname)
+                except OSError:
+                    pass
+                time.sleep(0.004 * random.randint(0, 4))
+
+        del_count = 500
+        log_count = 500
+
+        for delay in (False, True):
+            fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
+            os.close(fd)
+            remover = threading.Thread(target=remove_loop, args=(fn, del_count))
+            remover.daemon = True
+            remover.start()
+            h = logging.handlers.WatchedFileHandler(fn, delay=delay)
+            f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
+            h.setFormatter(f)
+            try:
+                for _ in range(log_count):
+                    time.sleep(0.005)
+                    r = logging.makeLogRecord({'msg': 'testing' })
+                    h.handle(r)
+            finally:
+                remover.join()
+                try:
+                    h.close()
+                except ValueError:
+                    pass
+                if os.path.exists(fn):
+                    os.unlink(fn)
+
+
 # Set the locale to the platform-dependent default.  I have no idea
 # why the test does this, but in any case we save the current locale
 # first and restore it at the end.
@@ -1882,7 +1925,7 @@
                  CustomLevelsAndFiltersTest, MemoryHandlerTest,
                  ConfigFileTest, SocketHandlerTest, MemoryTest,
                  EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
-                 ChildLoggerTest)
+                 ChildLoggerTest, HandlerTest)
 
 if __name__ == "__main__":
     test_main()
diff --git a/lib-python/2.7/test/test_long.py b/lib-python/2.7/test/test_long.py
--- a/lib-python/2.7/test/test_long.py
+++ b/lib-python/2.7/test/test_long.py
@@ -1,10 +1,11 @@
 import unittest
-from test import test_support
 import sys
 
 import random
 import math
 
+from test import test_int, test_support
+
 # Used for lazy formatting of failure messages
 class Frm(object):
     def __init__(self, format, *args):
@@ -78,8 +79,9 @@
         (unichr(0x200), ValueError),
 ]
 
+class LongTest(test_int.IntLongCommonTests, unittest.TestCase):
 
-class LongTest(unittest.TestCase):
+    ntype = long
 
     # Get quasi-random long consisting of ndigits digits (in base BASE).
     # quasi == the most-significant digit will not be 0, and the number
diff --git a/lib-python/2.7/test/test_mailbox.py b/lib-python/2.7/test/test_mailbox.py
--- a/lib-python/2.7/test/test_mailbox.py
+++ b/lib-python/2.7/test/test_mailbox.py
@@ -8,6 +8,7 @@
 import re
 import shutil
 import StringIO
+import tempfile
 from test import test_support
 import unittest
 import mailbox
@@ -20,7 +21,7 @@
 # Silence Py3k warning
 rfc822 = test_support.import_module('rfc822', deprecated=True)
 
-class TestBase(unittest.TestCase):
+class TestBase:
 
     def _check_sample(self, msg):
         # Inspect a mailbox.Message representation of the sample message
@@ -39,15 +40,15 @@
     def _delete_recursively(self, target):
         # Delete a file or delete a directory recursively
         if os.path.isdir(target):
-            shutil.rmtree(target)
+            test_support.rmtree(target)
         elif os.path.exists(target):
-            os.remove(target)
+            test_support.unlink(target)
 
 
 class TestMailbox(TestBase):
 
     _factory = None     # Overridden by subclasses to reuse tests
-    _template = 'From: foo\n\n%s'
+    _template = 'From: foo\n\n%s\n'
 
     def setUp(self):
         self._path = test_support.TESTFN
@@ -75,6 +76,18 @@
         for i in (1, 2, 3, 4):
             self._check_sample(self._box[keys[i]])
 
+    def test_add_file(self):
+        with tempfile.TemporaryFile('w+') as f:
+            f.write(_sample_message)
+            f.seek(0)
+            key = self._box.add(f)
+        self.assertEqual(self._box.get_string(key).split('\n'),
+            _sample_message.split('\n'))
+
+    def test_add_StringIO(self):
+        key = self._box.add(StringIO.StringIO(self._template % "0"))
+        self.assertEqual(self._box.get_string(key), self._template % "0")
+
     def test_remove(self):
         # Remove messages using remove()
         self._test_remove_or_delitem(self._box.remove)
@@ -124,7 +137,7 @@
         key0 = self._box.add(self._template % 0)
         msg = self._box.get(key0)
         self.assertEqual(msg['from'], 'foo')
-        self.assertEqual(msg.get_payload(), '0')
+        self.assertEqual(msg.get_payload(), '0\n')
         self.assertIs(self._box.get('foo'), None)
         self.assertFalse(self._box.get('foo', False))
         self._box.close()
@@ -132,14 +145,15 @@
         key1 = self._box.add(self._template % 1)
         msg = self._box.get(key1)
         self.assertEqual(msg['from'], 'foo')
-        self.assertEqual(msg.fp.read(), '1')
+        self.assertEqual(msg.fp.read(), '1' + os.linesep)
+        msg.fp.close()
 
     def test_getitem(self):
         # Retrieve message using __getitem__()
         key0 = self._box.add(self._template % 0)
         msg = self._box[key0]
         self.assertEqual(msg['from'], 'foo')
-        self.assertEqual(msg.get_payload(), '0')
+        self.assertEqual(msg.get_payload(), '0\n')
         self.assertRaises(KeyError, lambda: self._box['foo'])
         self._box.discard(key0)
         self.assertRaises(KeyError, lambda: self._box[key0])
@@ -151,7 +165,7 @@
         msg0 = self._box.get_message(key0)
         self.assertIsInstance(msg0, mailbox.Message)
         self.assertEqual(msg0['from'], 'foo')
-        self.assertEqual(msg0.get_payload(), '0')
+        self.assertEqual(msg0.get_payload(), '0\n')
         self._check_sample(self._box.get_message(key1))
 
     def test_get_string(self):
@@ -165,10 +179,14 @@
         # Get file representations of messages
         key0 = self._box.add(self._template % 0)
         key1 = self._box.add(_sample_message)
-        self.assertEqual(self._box.get_file(key0).read().replace(os.linesep, '\n'),
+        msg0 = self._box.get_file(key0)
+        self.assertEqual(msg0.read().replace(os.linesep, '\n'),
                          self._template % 0)
-        self.assertEqual(self._box.get_file(key1).read().replace(os.linesep, '\n'),
+        msg1 = self._box.get_file(key1)
+        self.assertEqual(msg1.read().replace(os.linesep, '\n'),
                          _sample_message)
+        msg0.close()
+        msg1.close()
 
     def test_get_file_can_be_closed_twice(self):
         # Issue 11700
@@ -320,15 +338,15 @@
         self.assertIn(key0, self._box)
         key1 = self._box.add(self._template % 1)
         self.assertIn(key1, self._box)
-        self.assertEqual(self._box.pop(key0).get_payload(), '0')
+        self.assertEqual(self._box.pop(key0).get_payload(), '0\n')
         self.assertNotIn(key0, self._box)
         self.assertIn(key1, self._box)
         key2 = self._box.add(self._template % 2)
         self.assertIn(key2, self._box)
-        self.assertEqual(self._box.pop(key2).get_payload(), '2')
+        self.assertEqual(self._box.pop(key2).get_payload(), '2\n')
         self.assertNotIn(key2, self._box)
         self.assertIn(key1, self._box)
-        self.assertEqual(self._box.pop(key1).get_payload(), '1')
+        self.assertEqual(self._box.pop(key1).get_payload(), '1\n')
         self.assertNotIn(key1, self._box)
         self.assertEqual(len(self._box), 0)
 
@@ -386,6 +404,17 @@
         # Write changes to disk
         self._test_flush_or_close(self._box.flush, True)
 
+    def test_popitem_and_flush_twice(self):
+        # See #15036.
+        self._box.add(self._template % 0)
+        self._box.add(self._template % 1)
+        self._box.flush()
+
+        self._box.popitem()
+        self._box.flush()
+        self._box.popitem()
+        self._box.flush()
+
     def test_lock_unlock(self):
         # Lock and unlock the mailbox
         self.assertFalse(os.path.exists(self._get_lock_path()))
@@ -403,6 +432,7 @@
         self._box.add(contents[0])
         self._box.add(contents[1])
         self._box.add(contents[2])
+        oldbox = self._box
         method()
         if should_call_close:
             self._box.close()
@@ -411,6 +441,7 @@
         self.assertEqual(len(keys), 3)
         for key in keys:
             self.assertIn(self._box.get_string(key), contents)
+        oldbox.close()
 
     def test_dump_message(self):
         # Write message representations to disk
@@ -429,7 +460,7 @@
         return self._path + '.lock'
 
 
-class TestMailboxSuperclass(TestBase):
+class TestMailboxSuperclass(TestBase, unittest.TestCase):
 
     def test_notimplemented(self):
         # Test that all Mailbox methods raise NotImplementedException.
@@ -464,7 +495,7 @@
         self.assertRaises(NotImplementedError, lambda: box.close())
 
 
-class TestMaildir(TestMailbox):
+class TestMaildir(TestMailbox, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.Maildir(path, factory)
 
@@ -506,7 +537,7 @@
         msg_returned = self._box.get_message(key)
         self.assertEqual(msg_returned.get_subdir(), 'new')
         self.assertEqual(msg_returned.get_flags(), '')
-        self.assertEqual(msg_returned.get_payload(), '1')
+        self.assertEqual(msg_returned.get_payload(), '1\n')
         msg2 = mailbox.MaildirMessage(self._template % 2)
         msg2.set_info('2,S')
         self._box[key] = msg2
@@ -514,7 +545,7 @@
         msg_returned = self._box.get_message(key)
         self.assertEqual(msg_returned.get_subdir(), 'new')
         self.assertEqual(msg_returned.get_flags(), 'S')
-        self.assertEqual(msg_returned.get_payload(), '3')
+        self.assertEqual(msg_returned.get_payload(), '3\n')
 
     def test_consistent_factory(self):
         # Add a message.
@@ -636,13 +667,13 @@
             self.assertTrue(match is not None, "Invalid file name: '%s'" % tail)
             groups = match.groups()
             if previous_groups is not None:
-                self.assertTrue(int(groups[0] >= previous_groups[0]),
+                self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]),
                              "Non-monotonic seconds: '%s' before '%s'" %
                              (previous_groups[0], groups[0]))
-                self.assertTrue(int(groups[1] >= previous_groups[1]) or
-                             groups[0] != groups[1],
-                             "Non-monotonic milliseconds: '%s' before '%s'" %
-                             (previous_groups[1], groups[1]))
+                if int(groups[0]) == int(previous_groups[0]):
+                    self.assertGreaterEqual(int(groups[1]), int(previous_groups[1]),
+                                "Non-monotonic milliseconds: '%s' before '%s'" %
+                                (previous_groups[1], groups[1]))
                 self.assertTrue(int(groups[2]) == pid,
                              "Process ID mismatch: '%s' should be '%s'" %
                              (groups[2], pid))
@@ -813,7 +844,49 @@
         self._box._refresh()
         self.assertTrue(refreshed())
 
-class _TestMboxMMDF(TestMailbox):
+
+class _TestSingleFile(TestMailbox):
+    '''Common tests for single-file mailboxes'''
+
+    def test_add_doesnt_rewrite(self):
+        # When only adding messages, flush() should not rewrite the
+        # mailbox file. See issue #9559.
+
+        # Inode number changes if the contents are written to another
+        # file which is then renamed over the original file. So we
+        # must check that the inode number doesn't change.
+        inode_before = os.stat(self._path).st_ino
+
+        self._box.add(self._template % 0)
+        self._box.flush()
+
+        inode_after = os.stat(self._path).st_ino
+        self.assertEqual(inode_before, inode_after)
+
+        # Make sure the message was really added
+        self._box.close()
+        self._box = self._factory(self._path)
+        self.assertEqual(len(self._box), 1)
+
+    def test_permissions_after_flush(self):
+        # See issue #5346
+
+        # Make the mailbox world writable. It's unlikely that the new
+        # mailbox file would have these permissions after flush(),
+        # because umask usually prevents it.
+        mode = os.stat(self._path).st_mode | 0o666
+        os.chmod(self._path, mode)
+
+        self._box.add(self._template % 0)
+        i = self._box.add(self._template % 1)
+        # Need to remove one message to make flush() create a new file
+        self._box.remove(i)
+        self._box.flush()
+
+        self.assertEqual(os.stat(self._path).st_mode, mode)
+
+
+class _TestMboxMMDF(_TestSingleFile):
 
     def tearDown(self):
         self._box.close()
@@ -823,14 +896,14 @@
 
     def test_add_from_string(self):
         # Add a string starting with 'From ' to the mailbox
-        key = self._box.add('From foo at bar blah\nFrom: foo\n\n0')
+        key = self._box.add('From foo at bar blah\nFrom: foo\n\n0\n')
         self.assertEqual(self._box[key].get_from(), 'foo at bar blah')
-        self.assertEqual(self._box[key].get_payload(), '0')
+        self.assertEqual(self._box[key].get_payload(), '0\n')
 
     def test_add_mbox_or_mmdf_message(self):
         # Add an mboxMessage or MMDFMessage
         for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
-            msg = class_('From foo at bar blah\nFrom: foo\n\n0')
+            msg = class_('From foo at bar blah\nFrom: foo\n\n0\n')
             key = self._box.add(msg)
 
     def test_open_close_open(self):
@@ -914,7 +987,7 @@
         self._box.close()
 
 
-class TestMbox(_TestMboxMMDF):
+class TestMbox(_TestMboxMMDF, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.mbox(path, factory)
 
@@ -937,12 +1010,35 @@
             perms = st.st_mode
             self.assertFalse((perms & 0111)) # Execute bits should all be off.
 
-class TestMMDF(_TestMboxMMDF):
+    def test_terminating_newline(self):
+        message = email.message.Message()
+        message['From'] = 'john at example.com'
+        message.set_payload('No newline at the end')
+        i = self._box.add(message)
+
+        # A newline should have been appended to the payload
+        message = self._box.get(i)
+        self.assertEqual(message.get_payload(), 'No newline at the end\n')
+
+    def test_message_separator(self):
+        # Check there's always a single blank line after each message
+        self._box.add('From: foo\n\n0')  # No newline at the end
+        with open(self._path) as f:
+            data = f.read()
+            self.assertEqual(data[-3:], '0\n\n')
+
+        self._box.add('From: foo\n\n0\n')  # Newline at the end
+        with open(self._path) as f:
+            data = f.read()
+            self.assertEqual(data[-3:], '0\n\n')
+
+
+class TestMMDF(_TestMboxMMDF, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.MMDF(path, factory)
 
 
-class TestMH(TestMailbox):
+class TestMH(TestMailbox, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.MH(path, factory)
 
@@ -1074,7 +1170,7 @@
         return os.path.join(self._path, '.mh_sequences.lock')
 
 
-class TestBabyl(TestMailbox):
+class TestBabyl(_TestSingleFile, unittest.TestCase):
 
     _factory = lambda self, path, factory=None: mailbox.Babyl(path, factory)
 
@@ -1103,7 +1199,7 @@
         self.assertEqual(set(self._box.get_labels()), set(['blah']))
 
 
-class TestMessage(TestBase):
+class TestMessage(TestBase, unittest.TestCase):
 
     _factory = mailbox.Message      # Overridden by subclasses to reuse tests
 
@@ -1174,7 +1270,7 @@
         pass
 
 
-class TestMaildirMessage(TestMessage):
+class TestMaildirMessage(TestMessage, unittest.TestCase):
 
     _factory = mailbox.MaildirMessage
 
@@ -1249,7 +1345,7 @@
         self._check_sample(msg)
 
 
-class _TestMboxMMDFMessage(TestMessage):
+class _TestMboxMMDFMessage:
 
     _factory = mailbox._mboxMMDFMessage
 
@@ -1296,12 +1392,12 @@
                                  r"\d{2} \d{4}", msg.get_from()))
 
 
-class TestMboxMessage(_TestMboxMMDFMessage):
+class TestMboxMessage(_TestMboxMMDFMessage, TestMessage):
 
     _factory = mailbox.mboxMessage
 
 
-class TestMHMessage(TestMessage):
+class TestMHMessage(TestMessage, unittest.TestCase):
 
     _factory = mailbox.MHMessage
 
@@ -1332,7 +1428,7 @@
         self.assertEqual(msg.get_sequences(), ['foobar', 'replied'])
 
 
-class TestBabylMessage(TestMessage):
+class TestBabylMessage(TestMessage, unittest.TestCase):
 
     _factory = mailbox.BabylMessage
 
@@ -1387,12 +1483,12 @@
             self.assertEqual(visible[header], msg[header])
 
 
-class TestMMDFMessage(_TestMboxMMDFMessage):
+class TestMMDFMessage(_TestMboxMMDFMessage, TestMessage):
 
     _factory = mailbox.MMDFMessage
 
 
-class TestMessageConversion(TestBase):
+class TestMessageConversion(TestBase, unittest.TestCase):
 
     def test_plain_to_x(self):
         # Convert Message to all formats
@@ -1715,7 +1811,7 @@
         proxy.close()
 
 
-class TestProxyFile(TestProxyFileBase):
+class TestProxyFile(TestProxyFileBase, unittest.TestCase):
 
     def setUp(self):
         self._path = test_support.TESTFN
@@ -1764,7 +1860,7 @@
         self._test_close(mailbox._ProxyFile(self._file))
 
 
-class TestPartialFile(TestProxyFileBase):
+class TestPartialFile(TestProxyFileBase, unittest.TestCase):
 
     def setUp(self):
         self._path = test_support.TESTFN
@@ -1831,6 +1927,10 @@
     def setUp(self):
         # create a new maildir mailbox to work with:
         self._dir = test_support.TESTFN
+        if os.path.isdir(self._dir):
+            test_support.rmtree(self._dir)
+        if os.path.isfile(self._dir):
+            test_support.unlink(self._dir)
         os.mkdir(self._dir)
         os.mkdir(os.path.join(self._dir, "cur"))
         os.mkdir(os.path.join(self._dir, "tmp"))
@@ -1840,10 +1940,10 @@
 
     def tearDown(self):
         map(os.unlink, self._msgfiles)
-        os.rmdir(os.path.join(self._dir, "cur"))
-        os.rmdir(os.path.join(self._dir, "tmp"))
-        os.rmdir(os.path.join(self._dir, "new"))
-        os.rmdir(self._dir)
+        test_support.rmdir(os.path.join(self._dir, "cur"))
+        test_support.rmdir(os.path.join(self._dir, "tmp"))
+        test_support.rmdir(os.path.join(self._dir, "new"))
+        test_support.rmdir(self._dir)
 
     def createMessage(self, dir, mbox=False):
         t = int(time.time() % 1000000)
@@ -1879,7 +1979,9 @@
         self.createMessage("cur")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         #self.assertTrue(len(self.mbox.boxes) == 1)
-        self.assertIsNot(self.mbox.next(), None)
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
         self.assertIs(self.mbox.next(), None)
         self.assertIs(self.mbox.next(), None)
 
@@ -1887,7 +1989,9 @@
         self.createMessage("new")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         #self.assertTrue(len(self.mbox.boxes) == 1)
-        self.assertIsNot(self.mbox.next(), None)
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
         self.assertIs(self.mbox.next(), None)
         self.assertIs(self.mbox.next(), None)
 
@@ -1896,8 +2000,12 @@
         self.createMessage("new")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         #self.assertTrue(len(self.mbox.boxes) == 2)
-        self.assertIsNot(self.mbox.next(), None)
-        self.assertIsNot(self.mbox.next(), None)
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
+        msg = self.mbox.next()
+        self.assertIsNot(msg, None)
+        msg.fp.close()
         self.assertIs(self.mbox.next(), None)
         self.assertIs(self.mbox.next(), None)
 
@@ -1906,11 +2014,13 @@
         import email.parser
         fname = self.createMessage("cur", True)
         n = 0
-        for msg in mailbox.PortableUnixMailbox(open(fname),
+        fid = open(fname)
+        for msg in mailbox.PortableUnixMailbox(fid,
                                                email.parser.Parser().parse):
             n += 1
             self.assertEqual(msg["subject"], "Simple Test")
             self.assertEqual(len(str(msg)), len(FROM_)+len(DUMMY_MESSAGE))
+        fid.close()
         self.assertEqual(n, 1)
 
 ## End: classes from the original module (for backward compatibility).
diff --git a/lib-python/2.7/test/test_marshal.py b/lib-python/2.7/test/test_marshal.py
--- a/lib-python/2.7/test/test_marshal.py
+++ b/lib-python/2.7/test/test_marshal.py
@@ -269,6 +269,53 @@
         invalid_string = 'l\x02\x00\x00\x00\x00\x00\x00\x00'
         self.assertRaises(ValueError, marshal.loads, invalid_string)
 
+LARGE_SIZE = 2**31
+character_size = 4 if sys.maxunicode > 0xFFFF else 2
+pointer_size = 8 if sys.maxsize > 0xFFFFFFFF else 4
+
+ at unittest.skipIf(LARGE_SIZE > sys.maxsize, "test cannot run on 32-bit systems")
+class LargeValuesTestCase(unittest.TestCase):
+    def check_unmarshallable(self, data):
+        f = open(test_support.TESTFN, 'wb')
+        self.addCleanup(test_support.unlink, test_support.TESTFN)
+        with f:
+            self.assertRaises(ValueError, marshal.dump, data, f)
+
+    @test_support.precisionbigmemtest(size=LARGE_SIZE, memuse=1, dry_run=False)
+    def test_string(self, size):
+        self.check_unmarshallable('x' * size)
+
+    @test_support.precisionbigmemtest(size=LARGE_SIZE,
+            memuse=character_size, dry_run=False)
+    def test_unicode(self, size):
+        self.check_unmarshallable(u'x' * size)
+
+    @test_support.precisionbigmemtest(size=LARGE_SIZE,
+            memuse=pointer_size, dry_run=False)
+    def test_tuple(self, size):
+        self.check_unmarshallable((None,) * size)
+
+    @test_support.precisionbigmemtest(size=LARGE_SIZE,
+            memuse=pointer_size, dry_run=False)
+    def test_list(self, size):
+        self.check_unmarshallable([None] * size)
+
+    @test_support.precisionbigmemtest(size=LARGE_SIZE,
+            memuse=pointer_size*12 + sys.getsizeof(LARGE_SIZE-1),
+            dry_run=False)
+    def test_set(self, size):
+        self.check_unmarshallable(set(range(size)))
+
+    @test_support.precisionbigmemtest(size=LARGE_SIZE,
+            memuse=pointer_size*12 + sys.getsizeof(LARGE_SIZE-1),
+            dry_run=False)
+    def test_frozenset(self, size):
+        self.check_unmarshallable(frozenset(range(size)))
+
+    @test_support.precisionbigmemtest(size=LARGE_SIZE, memuse=1, dry_run=False)
+    def test_bytearray(self, size):
+        self.check_unmarshallable(bytearray(size))
+
 
 def test_main():
     test_support.run_unittest(IntTestCase,
@@ -277,7 +324,9 @@
                               CodeTestCase,
                               ContainerTestCase,
                               ExceptionTestCase,
-                              BugsTestCase)
+                              BugsTestCase,
+                              LargeValuesTestCase,
+                             )
 
 if __name__ == "__main__":
     test_main()
diff --git a/lib-python/2.7/test/test_memoryio.py b/lib-python/2.7/test/test_memoryio.py
--- a/lib-python/2.7/test/test_memoryio.py
+++ b/lib-python/2.7/test/test_memoryio.py
@@ -328,9 +328,9 @@
         self.assertEqual(memio.isatty(), False)
         self.assertEqual(memio.closed, False)
         memio.close()
-        self.assertEqual(memio.writable(), True)
-        self.assertEqual(memio.readable(), True)
-        self.assertEqual(memio.seekable(), True)
+        self.assertRaises(ValueError, memio.writable)
+        self.assertRaises(ValueError, memio.readable)
+        self.assertRaises(ValueError, memio.seekable)
         self.assertRaises(ValueError, memio.isatty)
         self.assertEqual(memio.closed, True)
 
@@ -638,6 +638,16 @@
         memio.close()
         self.assertRaises(ValueError, memio.__setstate__, (b"closed", 0, None))
 
+    check_sizeof = support.check_sizeof
+
+    @support.cpython_only
+    def test_sizeof(self):
+        basesize = support.calcobjsize(b'P2PP2P')
+        check = self.check_sizeof
+        self.assertEqual(object.__sizeof__(io.BytesIO()), basesize)
+        check(io.BytesIO(), basesize )
+        check(io.BytesIO(b'a'), basesize + 1 + 1 )
+        check(io.BytesIO(b'a' * 1000), basesize + 1000 + 1 )
 
 class CStringIOTest(PyStringIOTest):
     ioclass = io.StringIO
diff --git a/lib-python/2.7/test/test_minidom.py b/lib-python/2.7/test/test_minidom.py
--- a/lib-python/2.7/test/test_minidom.py
+++ b/lib-python/2.7/test/test_minidom.py
@@ -1060,7 +1060,7 @@
                 '<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>',
                 "testEncodings - encoding EURO SIGN")
 
-        # Verify that character decoding errors throw exceptions instead
+        # Verify that character decoding errors raise exceptions instead
         # of crashing
         self.assertRaises(UnicodeDecodeError, parseString,
                 '<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
diff --git a/lib-python/2.7/test/test_mmap.py b/lib-python/2.7/test/test_mmap.py
--- a/lib-python/2.7/test/test_mmap.py
+++ b/lib-python/2.7/test/test_mmap.py
@@ -466,6 +466,19 @@
         f.flush ()
         return mmap.mmap (f.fileno(), 0)
 
+    def test_empty_file (self):
+        f = open (TESTFN, 'w+b')
+        f.close()
+        with open(TESTFN, "rb") as f :
+            try:
+                m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
+                m.close()
+                self.fail("should not have been able to mmap empty file")
+            except ValueError as e:
+                self.assertEqual(e.message, "cannot mmap an empty file")
+            except:
+                self.fail("unexpected exception: " + str(e))
+
     def test_offset (self):
         f = open (TESTFN, 'w+b')
 
@@ -669,6 +682,13 @@
 
     def test_large_filesize(self):
         with self._make_test_file(0x17FFFFFFF, b" ") as f:
+            if sys.maxsize < 0x180000000:
+                # On 32 bit platforms the file is larger than sys.maxsize so
+                # mapping the whole file should fail -- Issue #16743
+                with self.assertRaises(OverflowError):
+                    mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
+                with self.assertRaises(ValueError):
+                    mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
             m = mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ)
             try:
                 self.assertEqual(m.size(), 0x180000000)
diff --git a/lib-python/2.7/test/test_multiprocessing.py b/lib-python/2.7/test/test_multiprocessing.py
--- a/lib-python/2.7/test/test_multiprocessing.py
+++ b/lib-python/2.7/test/test_multiprocessing.py
@@ -16,6 +16,7 @@
 import random
 import logging
 import errno
+import test.script_helper
 from test import test_support
 from StringIO import StringIO
 _multiprocessing = test_support.import_module('_multiprocessing')
@@ -325,6 +326,36 @@
             ]
         self.assertEqual(result, expected)
 
+    @classmethod
+    def _test_sys_exit(cls, reason, testfn):
+        sys.stderr = open(testfn, 'w')
+        sys.exit(reason)
+
+    def test_sys_exit(self):
+        # See Issue 13854
+        if self.TYPE == 'threads':
+            return
+
+        testfn = test_support.TESTFN
+        self.addCleanup(test_support.unlink, testfn)
+
+        for reason, code in (([1, 2, 3], 1), ('ignore this', 0)):
+            p = self.Process(target=self._test_sys_exit, args=(reason, testfn))
+            p.daemon = True
+            p.start()
+            p.join(5)
+            self.assertEqual(p.exitcode, code)
+
+            with open(testfn, 'r') as f:
+                self.assertEqual(f.read().rstrip(), str(reason))
+
+        for reason in (True, False, 8):
+            p = self.Process(target=sys.exit, args=(reason,))
+            p.daemon = True
+            p.start()
+            p.join(5)
+            self.assertEqual(p.exitcode, reason)
+
 #
 #
 #
@@ -1152,6 +1183,36 @@
         join()
         self.assertTrue(join.elapsed < 0.2)
 
+    def test_empty_iterable(self):
+        # See Issue 12157
+        p = self.Pool(1)
+
+        self.assertEqual(p.map(sqr, []), [])
+        self.assertEqual(list(p.imap(sqr, [])), [])
+        self.assertEqual(list(p.imap_unordered(sqr, [])), [])
+        self.assertEqual(p.map_async(sqr, []).get(), [])
+
+        p.close()
+        p.join()
+
+def unpickleable_result():
+    return lambda: 42
+
+class _TestPoolWorkerErrors(BaseTestCase):
+    ALLOWED_TYPES = ('processes', )
+
+    def test_unpickleable_result(self):
+        from multiprocessing.pool import MaybeEncodingError
+        p = multiprocessing.Pool(2)
+
+        # Make sure we don't lose pool processes because of encoding errors.
+        for iteration in range(20):
+            res = p.apply_async(unpickleable_result)
+            self.assertRaises(MaybeEncodingError, res.get)
+
+        p.close()
+        p.join()
+
 class _TestPoolWorkerLifetime(BaseTestCase):
 
     ALLOWED_TYPES = ('processes', )
@@ -1452,6 +1513,7 @@
         self.assertTimingAlmostEqual(poll.elapsed, TIMEOUT1)
 
         conn.send(None)
+        time.sleep(.1)
 
         self.assertEqual(poll(TIMEOUT1), True)
         self.assertTimingAlmostEqual(poll.elapsed, 0)
@@ -1651,6 +1713,23 @@
             self.assertEqual(conn.recv(), 'hello')
             p.join()
             l.close()
+
+    def test_issue14725(self):
+        l = self.connection.Listener()
+        p = self.Process(target=self._test, args=(l.address,))
+        p.daemon = True
+        p.start()
+        time.sleep(1)
+        # On Windows the client process should by now have connected,
+        # written data and closed the pipe handle by now.  This causes
+        # ConnectNamdedPipe() to fail with ERROR_NO_DATA.  See Issue
+        # 14725.
+        conn = l.accept()
+        self.assertEqual(conn.recv(), 'hello')
+        conn.close()
+        p.join()
+        l.close()
+
 #
 # Test of sending connection and socket objects between processes
 #
@@ -2026,6 +2105,38 @@
 #         assert self.__handled
 
 #
+# Check that Process.join() retries if os.waitpid() fails with EINTR
+#
+
+class _TestPollEintr(BaseTestCase):
+
+    ALLOWED_TYPES = ('processes',)
+
+    @classmethod
+    def _killer(cls, pid):
+        time.sleep(0.5)
+        os.kill(pid, signal.SIGUSR1)
+
+    @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1')
+    def test_poll_eintr(self):
+        got_signal = [False]
+        def record(*args):
+            got_signal[0] = True
+        pid = os.getpid()
+        oldhandler = signal.signal(signal.SIGUSR1, record)
+        try:
+            killer = self.Process(target=self._killer, args=(pid,))
+            killer.start()
+            p = self.Process(target=time.sleep, args=(1,))
+            p.start()
+            p.join()
+            self.assertTrue(got_signal[0])
+            self.assertEqual(p.exitcode, 0)
+            killer.join()
+        finally:
+            signal.signal(signal.SIGUSR1, oldhandler)
+
+#
 # Test to verify handle verification, see issue 3321
 #
 
@@ -2078,7 +2189,7 @@
         'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore',
         'Condition', 'Event', 'Value', 'Array', 'RawValue',
         'RawArray', 'current_process', 'active_children', 'Pipe',
-        'connection', 'JoinableQueue'
+        'connection', 'JoinableQueue', 'Pool'
         )))
 
 testcases_processes = create_test_cases(ProcessesMixin, type='processes')
@@ -2092,7 +2203,7 @@
     locals().update(get_attributes(manager, (
         'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore',
        'Condition', 'Event', 'Value', 'Array', 'list', 'dict',
-        'Namespace', 'JoinableQueue'
+        'Namespace', 'JoinableQueue', 'Pool'
         )))
 
 testcases_manager = create_test_cases(ManagerMixin, type='manager')
@@ -2106,7 +2217,7 @@
         'Queue', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore',
         'Condition', 'Event', 'Value', 'Array', 'current_process',
         'active_children', 'Pipe', 'connection', 'dict', 'list',
-        'Namespace', 'JoinableQueue'
+        'Namespace', 'JoinableQueue', 'Pool'
         )))
 
 testcases_threads = create_test_cases(ThreadsMixin, type='threads')
@@ -2238,8 +2349,62 @@
         flike.flush()
         assert sio.getvalue() == 'foo'
 
+#
+# Test interaction with socket timeouts - see Issue #6056
+#
+
+class TestTimeouts(unittest.TestCase):
+    @classmethod
+    def _test_timeout(cls, child, address):
+        time.sleep(1)
+        child.send(123)
+        child.close()
+        conn = multiprocessing.connection.Client(address)
+        conn.send(456)
+        conn.close()
+
+    def test_timeout(self):
+        old_timeout = socket.getdefaulttimeout()
+        try:
+            socket.setdefaulttimeout(0.1)
+            parent, child = multiprocessing.Pipe(duplex=True)
+            l = multiprocessing.connection.Listener(family='AF_INET')
+            p = multiprocessing.Process(target=self._test_timeout,
+                                        args=(child, l.address))
+            p.start()
+            child.close()
+            self.assertEqual(parent.recv(), 123)
+            parent.close()
+            conn = l.accept()
+            self.assertEqual(conn.recv(), 456)
+            conn.close()
+            l.close()
+            p.join(10)
+        finally:
+            socket.setdefaulttimeout(old_timeout)
+
+#
+# Test what happens with no "if __name__ == '__main__'"
+#
+
+class TestNoForkBomb(unittest.TestCase):
+    def test_noforkbomb(self):
+        name = os.path.join(os.path.dirname(__file__), 'mp_fork_bomb.py')
+        if WIN32:
+            rc, out, err = test.script_helper.assert_python_failure(name)
+            self.assertEqual('', out.decode('ascii'))
+            self.assertIn('RuntimeError', err.decode('ascii'))
+        else:
+            rc, out, err = test.script_helper.assert_python_ok(name)
+            self.assertEqual('123', out.decode('ascii').rstrip())
+            self.assertEqual('', err.decode('ascii'))
+
+#
+#
+#
+
 testcases_other = [OtherTest, TestInvalidHandle, TestInitializers,
-                   TestStdinBadfiledescriptor]
+                   TestStdinBadfiledescriptor, TestTimeouts, TestNoForkBomb]
 
 #
 #
diff --git a/lib-python/2.7/test/test_mutex.py b/lib-python/2.7/test/test_mutex.py
--- a/lib-python/2.7/test/test_mutex.py
+++ b/lib-python/2.7/test/test_mutex.py
@@ -14,7 +14,7 @@
             m.lock(called_by_mutex2, "eggs")
 
         def called_by_mutex2(some_data):
-            self.assertEquals(some_data, "eggs")
+            self.assertEqual(some_data, "eggs")
             self.assertTrue(m.test(), "mutex not held")
             self.assertTrue(ready_for_2,
                          "called_by_mutex2 called too soon")
diff --git a/lib-python/2.7/test/test_old_mailbox.py b/lib-python/2.7/test/test_old_mailbox.py
--- a/lib-python/2.7/test/test_old_mailbox.py
+++ b/lib-python/2.7/test/test_old_mailbox.py
@@ -73,7 +73,9 @@
         self.createMessage("cur")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         self.assertTrue(len(self.mbox) == 1)
-        self.assertTrue(self.mbox.next() is not None)
+        msg = self.mbox.next()
+        self.assertTrue(msg is not None)
+        msg.fp.close()
         self.assertTrue(self.mbox.next() is None)
         self.assertTrue(self.mbox.next() is None)
 
@@ -81,7 +83,9 @@
         self.createMessage("new")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         self.assertTrue(len(self.mbox) == 1)
-        self.assertTrue(self.mbox.next() is not None)
+        msg = self.mbox.next()
+        self.assertTrue(msg is not None)
+        msg.fp.close()
         self.assertTrue(self.mbox.next() is None)
         self.assertTrue(self.mbox.next() is None)
 
@@ -90,8 +94,12 @@
         self.createMessage("new")
         self.mbox = mailbox.Maildir(test_support.TESTFN)
         self.assertTrue(len(self.mbox) == 2)
-        self.assertTrue(self.mbox.next() is not None)
-        self.assertTrue(self.mbox.next() is not None)
+        msg = self.mbox.next()
+        self.assertTrue(msg is not None)
+        msg.fp.close()
+        msg = self.mbox.next()
+        self.assertTrue(msg is not None)
+        msg.fp.close()
         self.assertTrue(self.mbox.next() is None)
         self.assertTrue(self.mbox.next() is None)
 
diff --git a/lib-python/2.7/test/test_optparse.py b/lib-python/2.7/test/test_optparse.py
--- a/lib-python/2.7/test/test_optparse.py
+++ b/lib-python/2.7/test/test_optparse.py
@@ -769,6 +769,13 @@
         self.assertParseFail(["-test"],
                              "no such option: -e")
 
+    def test_add_option_accepts_unicode(self):
+        self.parser.add_option(u"-u", u"--unicode", action="store_true")
+        self.assertParseOK(["-u"],
+                           {'a': None, 'boo': None, 'foo': None, 'unicode': True},
+                           [])
+
+
 class TestBool(BaseTest):
     def setUp(self):
         options = [make_option("-v",
diff --git a/lib-python/2.7/test/test_os.py b/lib-python/2.7/test/test_os.py
--- a/lib-python/2.7/test/test_os.py
+++ b/lib-python/2.7/test/test_os.py
@@ -214,33 +214,33 @@
 
         try:
             result[200]
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except IndexError:
             pass
 
         # Make sure that assignment fails
         try:
             result.st_mode = 1
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except (AttributeError, TypeError):
             pass
 
         try:
             result.st_rdev = 1
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except (AttributeError, TypeError):
             pass
 
         try:
             result.parrot = 1
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except AttributeError:
             pass
 
         # Use the stat_result constructor with a too-short tuple.
         try:
             result2 = os.stat_result((10,))
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except TypeError:
             pass
 
@@ -274,20 +274,20 @@
         # Make sure that assignment really fails
         try:
             result.f_bfree = 1
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except TypeError:
             pass
 
         try:
             result.parrot = 1
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except AttributeError:
             pass
 
         # Use the constructor with a too-short tuple.
         try:
             result2 = os.statvfs_result((10,))
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except TypeError:
             pass
 
diff --git a/lib-python/2.7/test/test_parser.py b/lib-python/2.7/test/test_parser.py
--- a/lib-python/2.7/test/test_parser.py
+++ b/lib-python/2.7/test/test_parser.py
@@ -1,7 +1,8 @@
 import parser
 import unittest
 import sys
-from test import test_support
+import struct
+from test import test_support as support
 
 #
 #  First, we test that we can generate trees from valid source fragments,
@@ -566,6 +567,17 @@
         st = parser.suite('a = u"\u1"')
         self.assertRaises(SyntaxError, parser.compilest, st)
 
+    def test_issue_9011(self):
+        # Issue 9011: compilation of an unary minus expression changed
+        # the meaning of the ST, so that a second compilation produced
+        # incorrect results.
+        st = parser.expr('-3')
+        code1 = parser.compilest(st)
+        self.assertEqual(eval(code1), -3)
+        code2 = parser.compilest(st)
+        self.assertEqual(eval(code2), -3)
+
+
 class ParserStackLimitTestCase(unittest.TestCase):
     """try to push the parser to/over it's limits.
     see http://bugs.python.org/issue1881 for a discussion
@@ -583,12 +595,57 @@
         print >>sys.stderr, "Expecting 's_push: parser stack overflow' in next line"
         self.assertRaises(MemoryError, parser.expr, e)
 
+class STObjectTestCase(unittest.TestCase):
+    """Test operations on ST objects themselves"""
+
+    check_sizeof = support.check_sizeof
+
+    @support.cpython_only
+    def test_sizeof(self):
+        def XXXROUNDUP(n):
+            if n <= 1:
+                return n
+            if n <= 128:
+                return (n + 3) & ~3
+            return 1 << (n - 1).bit_length()
+
+        basesize = support.calcobjsize('Pii')
+        nodesize = struct.calcsize('hP3iP0h')
+        def sizeofchildren(node):
+            if node is None:
+                return 0
+            res = 0
+            hasstr = len(node) > 1 and isinstance(node[-1], str)
+            if hasstr:
+                res += len(node[-1]) + 1
+            children = node[1:-1] if hasstr else node[1:]
+            if children:
+                res += XXXROUNDUP(len(children)) * nodesize
+                for child in children:
+                    res += sizeofchildren(child)
+            return res
+
+        def check_st_sizeof(st):
+            self.check_sizeof(st, basesize + nodesize +
+                                  sizeofchildren(st.totuple()))
+
+        check_st_sizeof(parser.expr('2 + 3'))
+        check_st_sizeof(parser.expr('2 + 3 + 4'))
+        check_st_sizeof(parser.suite('x = 2 + 3'))
+        check_st_sizeof(parser.suite(''))
+        check_st_sizeof(parser.suite('# -*- coding: utf-8 -*-'))
+        check_st_sizeof(parser.expr('[' + '2,' * 1000 + ']'))
+
+
+    # XXX tests for pickling and unpickling of ST objects should go here
+
 def test_main():
-    test_support.run_unittest(
+    support.run_unittest(
         RoundtripLegalSyntaxTestCase,
         IllegalSyntaxTestCase,
         CompileTestCase,
         ParserStackLimitTestCase,
+        STObjectTestCase,
     )
 
 
diff --git a/lib-python/2.7/test/test_pdb.py b/lib-python/2.7/test/test_pdb.py
--- a/lib-python/2.7/test/test_pdb.py
+++ b/lib-python/2.7/test/test_pdb.py
@@ -6,12 +6,69 @@
 import os
 import unittest
 import subprocess
+import textwrap
 
 from test import test_support
 # This little helper class is essential for testing pdb under doctest.
 from test_doctest import _FakeInput
 
 
+class PdbTestCase(unittest.TestCase):
+
+    def run_pdb(self, script, commands):
+        """Run 'script' lines with pdb and the pdb 'commands'."""
+        filename = 'main.py'
+        with open(filename, 'w') as f:
+            f.write(textwrap.dedent(script))
+        self.addCleanup(test_support.unlink, filename)
+        cmd = [sys.executable, '-m', 'pdb', filename]
+        stdout = stderr = None
+        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                                   stdin=subprocess.PIPE,
+                                   stderr=subprocess.STDOUT,
+                                   )
+        stdout, stderr = proc.communicate(commands)
+        proc.stdout.close()
+        proc.stdin.close()
+        return stdout, stderr
+
+    def test_issue13183(self):
+        script = """
+            from bar import bar
+
+            def foo():
+                bar()
+
+            def nope():
+                pass
+
+            def foobar():
+                foo()
+                nope()
+
+            foobar()
+        """
+        commands = """
+            from bar import bar
+            break bar
+            continue
+            step
+            step
+            quit
+        """
+        bar = """
+            def bar():
+                pass
+        """
+        with open('bar.py', 'w') as f:
+            f.write(textwrap.dedent(bar))
+        self.addCleanup(test_support.unlink, 'bar.py')
+        stdout, stderr = self.run_pdb(script, commands)
+        self.assertTrue(
+            any('main.py(5)foo()->None' in l for l in stdout.splitlines()),
+            'Fail to step into the caller after a return')
+
+
 class PdbTestInput(object):
     """Context manager that makes testing Pdb in doctests easier."""
 
@@ -309,7 +366,9 @@
 def test_main():
     from test import test_pdb
     test_support.run_doctest(test_pdb, verbosity=True)
-    test_support.run_unittest(ModuleInitTester)
+    test_support.run_unittest(
+        PdbTestCase,
+        ModuleInitTester)
 
 if __name__ == '__main__':
     test_main()
diff --git a/lib-python/2.7/test/test_peepholer.py b/lib-python/2.7/test/test_peepholer.py
--- a/lib-python/2.7/test/test_peepholer.py
+++ b/lib-python/2.7/test/test_peepholer.py
@@ -138,21 +138,22 @@
         self.assertIn('(1000)', asm)
 
     def test_binary_subscr_on_unicode(self):
-        # valid code get optimized
+        # unicode strings don't get optimized
         asm = dis_single('u"foo"[0]')
-        self.assertIn("(u'f')", asm)
-        self.assertNotIn('BINARY_SUBSCR', asm)
+        self.assertNotIn("(u'f')", asm)
+        self.assertIn('BINARY_SUBSCR', asm)
         asm = dis_single('u"\u0061\uffff"[1]')
-        self.assertIn("(u'\\uffff')", asm)
-        self.assertNotIn('BINARY_SUBSCR', asm)
+        self.assertNotIn("(u'\\uffff')", asm)
+        self.assertIn('BINARY_SUBSCR', asm)
 
-        # invalid code doesn't get optimized
         # out of range
         asm = dis_single('u"fuu"[10]')
         self.assertIn('BINARY_SUBSCR', asm)
         # non-BMP char (see #5057)
         asm = dis_single('u"\U00012345"[0]')
         self.assertIn('BINARY_SUBSCR', asm)
+        asm = dis_single('u"\U00012345abcdef"[3]')
+        self.assertIn('BINARY_SUBSCR', asm)
 
 
     def test_folding_of_unaryops_on_constants(self):
diff --git a/lib-python/2.7/test/test_pickle.py b/lib-python/2.7/test/test_pickle.py
--- a/lib-python/2.7/test/test_pickle.py
+++ b/lib-python/2.7/test/test_pickle.py
@@ -3,10 +3,11 @@
 
 from test import test_support
 
-from test.pickletester import AbstractPickleTests
-from test.pickletester import AbstractPickleModuleTests
-from test.pickletester import AbstractPersistentPicklerTests
-from test.pickletester import AbstractPicklerUnpicklerObjectTests
+from test.pickletester import (AbstractPickleTests,
+                               AbstractPickleModuleTests,
+                               AbstractPersistentPicklerTests,
+                               AbstractPicklerUnpicklerObjectTests,
+                               BigmemPickleTests)
 
 class PickleTests(AbstractPickleTests, AbstractPickleModuleTests):
 
@@ -66,6 +67,16 @@
     pickler_class = pickle.Pickler
     unpickler_class = pickle.Unpickler
 
+class PickleBigmemPickleTests(BigmemPickleTests):
+
+    def dumps(self, arg, proto=0, fast=0):
+        # Ignore fast
+        return pickle.dumps(arg, proto)
+
+    def loads(self, buf):
+        # Ignore fast
+        return pickle.loads(buf)
+
 
 def test_main():
     test_support.run_unittest(
@@ -73,6 +84,7 @@
         PicklerTests,
         PersPicklerTests,
         PicklerUnpicklerObjectTests,
+        PickleBigmemPickleTests,
     )
     test_support.run_doctest(pickle)
 
diff --git a/lib-python/2.7/test/test_poll.py b/lib-python/2.7/test/test_poll.py
--- a/lib-python/2.7/test/test_poll.py
+++ b/lib-python/2.7/test/test_poll.py
@@ -1,6 +1,7 @@
 # Test case for the os.poll() function
 
 import os, select, random, unittest
+import _testcapi
 from test.test_support import TESTFN, run_unittest
 
 try:
@@ -150,6 +151,15 @@
         if x != 5:
             self.fail('Overflow must have occurred')
 
+        pollster = select.poll()
+        # Issue 15989
+        self.assertRaises(OverflowError, pollster.register, 0,
+                          _testcapi.SHRT_MAX + 1)
+        self.assertRaises(OverflowError, pollster.register, 0,
+                          _testcapi.USHRT_MAX + 1)
+        self.assertRaises(OverflowError, pollster.poll, _testcapi.INT_MAX + 1)
+        self.assertRaises(OverflowError, pollster.poll, _testcapi.UINT_MAX + 1)
+
 def test_main():
     run_unittest(PollTests)
 
diff --git a/lib-python/2.7/test/test_posix.py b/lib-python/2.7/test/test_posix.py
--- a/lib-python/2.7/test/test_posix.py
+++ b/lib-python/2.7/test/test_posix.py
@@ -9,6 +9,7 @@
 import sys
 import time
 import os
+import platform
 import pwd
 import shutil
 import stat
@@ -107,7 +108,11 @@
         # If a non-privileged user invokes it, it should fail with OSError
         # EPERM.
         if os.getuid() != 0:
-            name = pwd.getpwuid(posix.getuid()).pw_name
+            try:
+                name = pwd.getpwuid(posix.getuid()).pw_name
+            except KeyError:
+                # the current UID may not have a pwd entry
+                raise unittest.SkipTest("need a pwd entry")
             try:
                 posix.initgroups(name, 13)
             except OSError as e:
@@ -217,26 +222,64 @@
         if hasattr(posix, 'stat'):
             self.assertTrue(posix.stat(test_support.TESTFN))
 
-    def _test_all_chown_common(self, chown_func, first_param):
+    def _test_all_chown_common(self, chown_func, first_param, stat_func):
         """Common code for chown, fchown and lchown tests."""
-        if os.getuid() == 0:
-            try:
-                # Many linux distros have a nfsnobody user as MAX_UID-2
-                # that makes a good test case for signedness issues.
-                #   http://bugs.python.org/issue1747858
-                # This part of the test only runs when run as root.
-                # Only scary people run their tests as root.
-                ent = pwd.getpwnam('nfsnobody')
-                chown_func(first_param, ent.pw_uid, ent.pw_gid)
-            except KeyError:
-                pass
+        def check_stat(uid, gid):
+            if stat_func is not None:
+                stat = stat_func(first_param)
+                self.assertEqual(stat.st_uid, uid)
+                self.assertEqual(stat.st_gid, gid)
+        uid = os.getuid()
+        gid = os.getgid()
+        # test a successful chown call
+        chown_func(first_param, uid, gid)
+        check_stat(uid, gid)
+        chown_func(first_param, -1, gid)
+        check_stat(uid, gid)
+        chown_func(first_param, uid, -1)
+        check_stat(uid, gid)
+
+        if uid == 0:
+            # Try an amusingly large uid/gid to make sure we handle
+            # large unsigned values.  (chown lets you use any
+            # uid/gid you like, even if they aren't defined.)
+            #
+            # This problem keeps coming up:
+            #   http://bugs.python.org/issue1747858
+            #   http://bugs.python.org/issue4591
+            #   http://bugs.python.org/issue15301
+            # Hopefully the fix in 4591 fixes it for good!
+            #
+            # This part of the test only runs when run as root.
+            # Only scary people run their tests as root.
+
+            big_value = 2**31
+            chown_func(first_param, big_value, big_value)
+            check_stat(big_value, big_value)
+            chown_func(first_param, -1, -1)
+            check_stat(big_value, big_value)
+            chown_func(first_param, uid, gid)
+            check_stat(uid, gid)
+        elif platform.system() in ('HP-UX', 'SunOS'):
+            # HP-UX and Solaris can allow a non-root user to chown() to root
+            # (issue #5113)
+            raise unittest.SkipTest("Skipping because of non-standard chown() "
+                                    "behavior")
         else:
             # non-root cannot chown to root, raises OSError
-            self.assertRaises(OSError, chown_func,
-                              first_param, 0, 0)
-
-        # test a successful chown call
-        chown_func(first_param, os.getuid(), os.getgid())
+            self.assertRaises(OSError, chown_func, first_param, 0, 0)
+            check_stat(uid, gid)
+            self.assertRaises(OSError, chown_func, first_param, 0, -1)
+            check_stat(uid, gid)
+            if 0 not in os.getgroups():
+                self.assertRaises(OSError, chown_func, first_param, -1, 0)
+                check_stat(uid, gid)
+        # test illegal types
+        for t in str, float:
+            self.assertRaises(TypeError, chown_func, first_param, t(uid), gid)
+            check_stat(uid, gid)
+            self.assertRaises(TypeError, chown_func, first_param, uid, t(gid))
+            check_stat(uid, gid)
 
     @unittest.skipUnless(hasattr(posix, 'chown'), "test needs os.chown()")
     def test_chown(self):
@@ -246,7 +289,8 @@
 
         # re-create the file
         open(test_support.TESTFN, 'w').close()
-        self._test_all_chown_common(posix.chown, test_support.TESTFN)
+        self._test_all_chown_common(posix.chown, test_support.TESTFN,
+                                    getattr(posix, 'stat', None))
 
     @unittest.skipUnless(hasattr(posix, 'fchown'), "test needs os.fchown()")
     def test_fchown(self):
@@ -256,7 +300,8 @@
         test_file = open(test_support.TESTFN, 'w')
         try:
             fd = test_file.fileno()
-            self._test_all_chown_common(posix.fchown, fd)
+            self._test_all_chown_common(posix.fchown, fd,
+                                        getattr(posix, 'fstat', None))
         finally:
             test_file.close()
 
@@ -265,7 +310,8 @@
         os.unlink(test_support.TESTFN)
         # create a symlink
         os.symlink(_DUMMY_SYMLINK, test_support.TESTFN)
-        self._test_all_chown_common(posix.lchown, test_support.TESTFN)
+        self._test_all_chown_common(posix.lchown, test_support.TESTFN,
+                                    getattr(posix, 'lstat', None))
 
     def test_chdir(self):
         if hasattr(posix, 'chdir'):
@@ -324,7 +370,16 @@
     def _test_chflags_regular_file(self, chflags_func, target_file):
         st = os.stat(target_file)
         self.assertTrue(hasattr(st, 'st_flags'))
-        chflags_func(target_file, st.st_flags | stat.UF_IMMUTABLE)
+
+        # ZFS returns EOPNOTSUPP when attempting to set flag UF_IMMUTABLE.
+        try:
+            chflags_func(target_file, st.st_flags | stat.UF_IMMUTABLE)
+        except OSError as err:
+            if err.errno != errno.EOPNOTSUPP:
+                raise
+            msg = 'chflag UF_IMMUTABLE not supported by underlying fs'
+            self.skipTest(msg)
+
         try:
             new_st = os.stat(target_file)
             self.assertEqual(st.st_flags | stat.UF_IMMUTABLE, new_st.st_flags)
@@ -353,8 +408,16 @@
         self.teardown_files.append(_DUMMY_SYMLINK)
         dummy_symlink_st = os.lstat(_DUMMY_SYMLINK)
 
-        posix.lchflags(_DUMMY_SYMLINK,
-                       dummy_symlink_st.st_flags | stat.UF_IMMUTABLE)
+        # ZFS returns EOPNOTSUPP when attempting to set flag UF_IMMUTABLE.
+        try:
+            posix.lchflags(_DUMMY_SYMLINK,
+                           dummy_symlink_st.st_flags | stat.UF_IMMUTABLE)
+        except OSError as err:
+            if err.errno != errno.EOPNOTSUPP:
+                raise
+            msg = 'chflag UF_IMMUTABLE not supported by underlying fs'
+            self.skipTest(msg)
+
         try:
             new_testfn_st = os.stat(test_support.TESTFN)
             new_dummy_symlink_st = os.lstat(_DUMMY_SYMLINK)
@@ -395,8 +458,16 @@
                             _create_and_do_getcwd(dirname, current_path_length + len(dirname) + 1)
                     except OSError as e:
                         expected_errno = errno.ENAMETOOLONG
-                        if 'sunos' in sys.platform or 'openbsd' in sys.platform:
-                            expected_errno = errno.ERANGE # Issue 9185
+                        # The following platforms have quirky getcwd()
+                        # behaviour -- see issue 9185 and 15765 for
+                        # more information.
+                        quirky_platform = (
+                            'sunos' in sys.platform or
+                            'netbsd' in sys.platform or
+                            'openbsd' in sys.platform
+                        )
+                        if quirky_platform:
+                            expected_errno = errno.ERANGE
                         self.assertEqual(e.errno, expected_errno)
                     finally:
                         os.chdir('..')
@@ -412,10 +483,18 @@
     def test_getgroups(self):
         with os.popen('id -G') as idg:
             groups = idg.read().strip()
+            ret = idg.close()
 
-        if not groups:
+        if ret != None or not groups:
             raise unittest.SkipTest("need working 'id -G'")
 
+        # Issues 16698: OS X ABIs prior to 10.6 have limits on getgroups()
+        if sys.platform == 'darwin':
+            import sysconfig
+            dt = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') or '10.0'
+            if float(dt) < 10.6:
+                raise unittest.SkipTest("getgroups(2) is broken prior to 10.6")
+
         # 'id -G' and 'os.getgroups()' should return the same
         # groups, ignoring order and duplicates.
         # #10822 - it is implementation defined whether posix.getgroups()
diff --git a/lib-python/2.7/test/test_posixpath.py b/lib-python/2.7/test/test_posixpath.py
--- a/lib-python/2.7/test/test_posixpath.py
+++ b/lib-python/2.7/test/test_posixpath.py
@@ -9,6 +9,16 @@
 
 ABSTFN = abspath(test_support.TESTFN)
 
+def skip_if_ABSTFN_contains_backslash(test):
+    """
+    On Windows, posixpath.abspath still returns paths with backslashes
+    instead of posix forward slashes. If this is the case, several tests
+    fail, so skip them.
+    """
+    found_backslash = '\\' in ABSTFN
+    msg = "ABSTFN is not a posix path - tests fail"
+    return [test, unittest.skip(msg)(test)][found_backslash]
+
 def safe_rmdir(dirname):
     try:
         os.rmdir(dirname)
@@ -110,8 +120,10 @@
                 ),
                 True
             )
-            # If we don't have links, assume that os.stat doesn't return resonable
-            # inode information and thus, that samefile() doesn't work
+
+            # If we don't have links, assume that os.stat doesn't return
+            # reasonable inode information and thus, that samefile() doesn't
+            # work.
             if hasattr(os, "symlink"):
                 os.symlink(
                     test_support.TESTFN + "1",
@@ -151,19 +163,19 @@
                 ),
                 True
             )
-            # If we don't have links, assume that os.stat() doesn't return resonable
-            # inode information and thus, that samefile() doesn't work
+            # If we don't have links, assume that os.stat() doesn't return
+            # reasonable inode information and thus, that samestat() doesn't
+            # work.
             if hasattr(os, "symlink"):
-                if hasattr(os, "symlink"):
-                    os.symlink(test_support.TESTFN + "1", test_support.TESTFN + "2")
-                    self.assertIs(
-                        posixpath.samestat(
-                            os.stat(test_support.TESTFN + "1"),
-                            os.stat(test_support.TESTFN + "2")
-                        ),
-                        True
-                    )
-                    os.remove(test_support.TESTFN + "2")
+                os.symlink(test_support.TESTFN + "1", test_support.TESTFN + "2")
+                self.assertIs(
+                    posixpath.samestat(
+                        os.stat(test_support.TESTFN + "1"),
+                        os.stat(test_support.TESTFN + "2")
+                    ),
+                    True
+                )
+                os.remove(test_support.TESTFN + "2")
                 f = open(test_support.TESTFN + "2", "wb")
                 f.write("bar")
                 f.close()
@@ -201,6 +213,7 @@
             with test_support.EnvironmentVarGuard() as env:
                 env['HOME'] = '/'
                 self.assertEqual(posixpath.expanduser("~"), "/")
+                self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
 
     def test_normpath(self):
         self.assertEqual(posixpath.normpath(""), ".")
@@ -211,6 +224,18 @@
         self.assertEqual(posixpath.normpath("///foo/.//bar//.//..//.//baz"), "/foo/baz")
         self.assertEqual(posixpath.normpath("///..//./foo/.//bar"), "/foo/bar")
 
+    @skip_if_ABSTFN_contains_backslash
+    def test_realpath_curdir(self):
+        self.assertEqual(realpath('.'), os.getcwd())
+        self.assertEqual(realpath('./.'), os.getcwd())
+        self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd())
+
+    @skip_if_ABSTFN_contains_backslash
+    def test_realpath_pardir(self):
+        self.assertEqual(realpath('..'), dirname(os.getcwd()))
+        self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd())))
+        self.assertEqual(realpath('/'.join(['..'] * 100)), '/')
+
     if hasattr(os, "symlink"):
         def test_realpath_basic(self):
             # Basic operation.
@@ -233,6 +258,22 @@
                 self.assertEqual(realpath(ABSTFN+"1"), ABSTFN+"1")
                 self.assertEqual(realpath(ABSTFN+"2"), ABSTFN+"2")
 
+                self.assertEqual(realpath(ABSTFN+"1/x"), ABSTFN+"1/x")
+                self.assertEqual(realpath(ABSTFN+"1/.."), dirname(ABSTFN))
+                self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
+                os.symlink(ABSTFN+"x", ABSTFN+"y")
+                self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
+                                ABSTFN + "y")
+                self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
+                                ABSTFN + "1")
+
+                os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
+                self.assertEqual(realpath(ABSTFN+"a"), ABSTFN+"a/b")
+
+                os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
+                        basename(ABSTFN) + "c", ABSTFN+"c")
+                self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c")
+
                 # Test using relative path as well.
                 os.chdir(dirname(ABSTFN))
                 self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
@@ -241,6 +282,40 @@
                 test_support.unlink(ABSTFN)
                 test_support.unlink(ABSTFN+"1")
                 test_support.unlink(ABSTFN+"2")
+                test_support.unlink(ABSTFN+"y")
+                test_support.unlink(ABSTFN+"c")
+                test_support.unlink(ABSTFN+"a")
+
+        def test_realpath_repeated_indirect_symlinks(self):
+            # Issue #6975.
+            try:
+                os.mkdir(ABSTFN)
+                os.symlink('../' + basename(ABSTFN), ABSTFN + '/self')
+                os.symlink('self/self/self', ABSTFN + '/link')
+                self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN)
+            finally:
+                test_support.unlink(ABSTFN + '/self')
+                test_support.unlink(ABSTFN + '/link')
+                safe_rmdir(ABSTFN)
+
+        def test_realpath_deep_recursion(self):
+            depth = 10
+            old_path = abspath('.')
+            try:
+                os.mkdir(ABSTFN)
+                for i in range(depth):
+                    os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1))
+                os.symlink('.', ABSTFN + '/0')
+                self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
+
+                # Test using relative path as well.
+                os.chdir(ABSTFN)
+                self.assertEqual(realpath('%d' % depth), ABSTFN)
+            finally:
+                os.chdir(old_path)
+                for i in range(depth + 1):
+                    test_support.unlink(ABSTFN + '/%d' % i)
+                safe_rmdir(ABSTFN)
 
         def test_realpath_resolve_parents(self):
             # We also need to resolve any symlinks in the parents of a relative
diff --git a/lib-python/2.7/test/test_property.py b/lib-python/2.7/test/test_property.py
--- a/lib-python/2.7/test/test_property.py
+++ b/lib-python/2.7/test/test_property.py
@@ -163,7 +163,7 @@
             Foo.spam.__doc__,
             "spam wrapped in property subclass")
 
-    @unittest.skipIf(sys.flags.optimize <= 2,
+    @unittest.skipIf(sys.flags.optimize >= 2,
                      "Docstrings are omitted with -O2 and above")
     def test_property_setter_copies_getter_docstring(self):
         class Foo(object):
@@ -196,7 +196,7 @@
             FooSub.spam.__doc__,
             "spam wrapped in property subclass")
 
-    @unittest.skipIf(sys.flags.optimize <= 2,
+    @unittest.skipIf(sys.flags.optimize >= 2,
                      "Docstrings are omitted with -O2 and above")
     def test_property_new_getter_new_docstring(self):
 
diff --git a/lib-python/2.7/test/test_pty.py b/lib-python/2.7/test/test_pty.py
--- a/lib-python/2.7/test/test_pty.py
+++ b/lib-python/2.7/test/test_pty.py
@@ -152,7 +152,7 @@
             # platform-dependent amount of data is written to its fd.  On
             # Linux 2.6, it's 4000 bytes and the child won't block, but on OS
             # X even the small writes in the child above will block it.  Also
-            # on Linux, the read() will throw an OSError (input/output error)
+            # on Linux, the read() will raise an OSError (input/output error)
             # when it tries to read past the end of the buffer but the child's
             # already exited, so catch and discard those exceptions.  It's not
             # worth checking for EIO.
diff --git a/lib-python/2.7/test/test_pwd.py b/lib-python/2.7/test/test_pwd.py
--- a/lib-python/2.7/test/test_pwd.py
+++ b/lib-python/2.7/test/test_pwd.py
@@ -49,7 +49,9 @@
 
     def test_errors(self):
         self.assertRaises(TypeError, pwd.getpwuid)
+        self.assertRaises(TypeError, pwd.getpwuid, 3.14)
         self.assertRaises(TypeError, pwd.getpwnam)
+        self.assertRaises(TypeError, pwd.getpwnam, 42)
         self.assertRaises(TypeError, pwd.getpwall, 42)
 
         # try to get some errors
@@ -93,6 +95,13 @@
         self.assertNotIn(fakeuid, byuids)
         self.assertRaises(KeyError, pwd.getpwuid, fakeuid)
 
+        # -1 shouldn't be a valid uid because it has a special meaning in many
+        # uid-related functions
+        self.assertRaises(KeyError, pwd.getpwuid, -1)
+        # should be out of uid_t range
+        self.assertRaises(KeyError, pwd.getpwuid, 2**128)
+        self.assertRaises(KeyError, pwd.getpwuid, -2**128)
+
 def test_main():
     test_support.run_unittest(PwdTest)
 
diff --git a/lib-python/2.7/test/test_pyclbr.py b/lib-python/2.7/test/test_pyclbr.py
--- a/lib-python/2.7/test/test_pyclbr.py
+++ b/lib-python/2.7/test/test_pyclbr.py
@@ -188,6 +188,11 @@
         cm('email.parser')
         cm('test.test_pyclbr')
 
+    def test_issue_14798(self):
+        # test ImportError is raised when the first part of a dotted name is
+        # not a package
+        self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncore.foo')
+
 
 def test_main():
     run_unittest(PyclbrTest)
diff --git a/lib-python/2.7/test/test_pydoc.py b/lib-python/2.7/test/test_pydoc.py
--- a/lib-python/2.7/test/test_pydoc.py
+++ b/lib-python/2.7/test/test_pydoc.py
@@ -16,6 +16,14 @@
 
 from test import pydoc_mod
 
+if test.test_support.HAVE_DOCSTRINGS:
+    expected_data_docstrings = (
+        'dictionary for instance variables (if defined)',
+        'list of weak references to the object (if defined)',
+        )
+else:
+    expected_data_docstrings = ('', '')
+
 expected_text_pattern = \
 """
 NAME
@@ -40,11 +48,9 @@
     class B(__builtin__.object)
      |  Data descriptors defined here:
      |\x20\x20
-     |  __dict__
-     |      dictionary for instance variables (if defined)
+     |  __dict__%s
      |\x20\x20
-     |  __weakref__
-     |      list of weak references to the object (if defined)
+     |  __weakref__%s
      |\x20\x20
      |  ----------------------------------------------------------------------
      |  Data and other attributes defined here:
@@ -75,6 +81,9 @@
     Nobody
 """.strip()
 
+expected_text_data_docstrings = tuple('\n     |      ' + s if s else ''
+                                      for s in expected_data_docstrings)
+
 expected_html_pattern = \
 """
 <table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
@@ -121,10 +130,10 @@
 <tr><td bgcolor="#ffc8d8"><tt>   </tt></td><td> </td>
 <td width="100%%">Data descriptors defined here:<br>
 <dl><dt><strong>__dict__</strong></dt>
-<dd><tt>dictionary for instance variables (if defined)</tt></dd>
+<dd><tt>%s</tt></dd>
 </dl>
 <dl><dt><strong>__weakref__</strong></dt>
-<dd><tt>list of weak references to the object (if defined)</tt></dd>
+<dd><tt>%s</tt></dd>
 </dl>
 <hr>
 Data and other attributes defined here:<br>
@@ -168,6 +177,8 @@
 <td width="100%%">Nobody</td></tr></table>
 """.strip()
 
+expected_html_data_docstrings = tuple(s.replace(' ', ' ')
+                                      for s in expected_data_docstrings)
 
 # output pattern for missing module
 missing_pattern = "no Python documentation found for '%s'"
@@ -229,7 +240,9 @@
             mod_url = nturl2path.pathname2url(mod_file)
         else:
             mod_url = mod_file
-        expected_html = expected_html_pattern % (mod_url, mod_file, doc_loc)
+        expected_html = expected_html_pattern % (
+                        (mod_url, mod_file, doc_loc) +
+                        expected_html_data_docstrings)
         if result != expected_html:
             print_diffs(expected_html, result)
             self.fail("outputs are not equal, see diff above")
@@ -238,8 +251,9 @@
                      "Docstrings are omitted with -O2 and above")
     def test_text_doc(self):
         result, doc_loc = get_pydoc_text(pydoc_mod)
-        expected_text = expected_text_pattern % \
-                        (inspect.getabsfile(pydoc_mod), doc_loc)
+        expected_text = expected_text_pattern % (
+                        (inspect.getabsfile(pydoc_mod), doc_loc) +
+                        expected_text_data_docstrings)
         if result != expected_text:
             print_diffs(expected_text, result)
             self.fail("outputs are not equal, see diff above")
@@ -249,6 +263,17 @@
         result, doc_loc = get_pydoc_text(xml.etree)
         self.assertEqual(doc_loc, "", "MODULE DOCS incorrectly includes a link")
 
+    def test_non_str_name(self):
+        # issue14638
+        # Treat illegal (non-str) name like no name
+        class A:
+            __name__ = 42
+        class B:
+            pass
+        adoc = pydoc.render_doc(A())
+        bdoc = pydoc.render_doc(B())
+        self.assertEqual(adoc.replace("A", "B"), bdoc)
+
     def test_not_here(self):
         missing_module = "test.i_am_not_here"
         result = run_pydoc(missing_module)
diff --git a/lib-python/2.7/test/test_pyexpat.py b/lib-python/2.7/test/test_pyexpat.py
--- a/lib-python/2.7/test/test_pyexpat.py
+++ b/lib-python/2.7/test/test_pyexpat.py
@@ -588,6 +588,58 @@
         except expat.ExpatError as e:
             self.assertEqual(str(e), 'XML declaration not well-formed: line 1, column 14')
 
+class ForeignDTDTests(unittest.TestCase):
+    """
+    Tests for the UseForeignDTD method of expat parser objects.
+    """
+    def test_use_foreign_dtd(self):
+        """
+        If UseForeignDTD is passed True and a document without an external
+        entity reference is parsed, ExternalEntityRefHandler is first called
+        with None for the public and system ids.
+        """
+        handler_call_args = []
+        def resolve_entity(context, base, system_id, public_id):
+            handler_call_args.append((public_id, system_id))
+            return 1
+
+        parser = expat.ParserCreate()
+        parser.UseForeignDTD(True)
+        parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_ALWAYS)
+        parser.ExternalEntityRefHandler = resolve_entity
+        parser.Parse("<?xml version='1.0'?><element/>")
+        self.assertEqual(handler_call_args, [(None, None)])
+
+        # test UseForeignDTD() is equal to UseForeignDTD(True)
+        handler_call_args[:] = []
+
+        parser = expat.ParserCreate()
+        parser.UseForeignDTD()
+        parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_ALWAYS)
+        parser.ExternalEntityRefHandler = resolve_entity
+        parser.Parse("<?xml version='1.0'?><element/>")
+        self.assertEqual(handler_call_args, [(None, None)])
+
+    def test_ignore_use_foreign_dtd(self):
+        """
+        If UseForeignDTD is passed True and a document with an external
+        entity reference is parsed, ExternalEntityRefHandler is called with
+        the public and system ids from the document.
+        """
+        handler_call_args = []
+        def resolve_entity(context, base, system_id, public_id):
+            handler_call_args.append((public_id, system_id))
+            return 1
+
+        parser = expat.ParserCreate()
+        parser.UseForeignDTD(True)
+        parser.SetParamEntityParsing(expat.XML_PARAM_ENTITY_PARSING_ALWAYS)
+        parser.ExternalEntityRefHandler = resolve_entity
+        parser.Parse(
+            "<?xml version='1.0'?><!DOCTYPE foo PUBLIC 'bar' 'baz'><element/>")
+        self.assertEqual(handler_call_args, [("bar", "baz")])
+
+
 def test_main():
     run_unittest(SetAttributeTest,
                  ParseTest,
@@ -598,7 +650,8 @@
                  PositionTest,
                  sf1296433Test,
                  ChardataBufferTest,
-                 MalformedInputText)
+                 MalformedInputText,
+                 ForeignDTDTests)
 
 if __name__ == "__main__":
     test_main()
diff --git a/lib-python/2.7/test/test_random.py b/lib-python/2.7/test/test_random.py
--- a/lib-python/2.7/test/test_random.py
+++ b/lib-python/2.7/test/test_random.py
@@ -57,6 +57,14 @@
             self.assertRaises(TypeError, self.gen.jumpahead)  # needs an arg
             self.assertRaises(TypeError, self.gen.jumpahead, 2, 3)  # too many
 
+    def test_jumpahead_produces_valid_state(self):
+        # From http://bugs.python.org/issue14591.
+        self.gen.seed(199210368)
+        self.gen.jumpahead(13550674232554645900)
+        for i in range(500):
+            val = self.gen.random()
+            self.assertLess(val, 1.0)
+
     def test_sample(self):
         # For the entire allowable range of 0 <= k <= N, validate that
         # the sample is of the correct length and contains only unique items
@@ -486,6 +494,7 @@
         g.random = x[:].pop; g.paretovariate(1.0)
         g.random = x[:].pop; g.expovariate(1.0)
         g.random = x[:].pop; g.weibullvariate(1.0, 1.0)
+        g.random = x[:].pop; g.vonmisesvariate(1.0, 1.0)
         g.random = x[:].pop; g.normalvariate(0.0, 1.0)
         g.random = x[:].pop; g.gauss(0.0, 1.0)
         g.random = x[:].pop; g.lognormvariate(0.0, 1.0)
@@ -506,6 +515,7 @@
                 (g.uniform, (1.0,10.0), (10.0+1.0)/2, (10.0-1.0)**2/12),
                 (g.triangular, (0.0, 1.0, 1.0/3.0), 4.0/9.0, 7.0/9.0/18.0),
                 (g.expovariate, (1.5,), 1/1.5, 1/1.5**2),
+                (g.vonmisesvariate, (1.23, 0), pi, pi**2/3),
                 (g.paretovariate, (5.0,), 5.0/(5.0-1),
                                   5.0/((5.0-1)**2*(5.0-2))),
                 (g.weibullvariate, (1.0, 3.0), gamma(1+1/3.0),
@@ -522,8 +532,50 @@
                 s1 += e
                 s2 += (e - mu) ** 2
             N = len(y)
-            self.assertAlmostEqual(s1/N, mu, 2)
-            self.assertAlmostEqual(s2/(N-1), sigmasqrd, 2)
+            self.assertAlmostEqual(s1/N, mu, places=2,
+                                   msg='%s%r' % (variate.__name__, args))
+            self.assertAlmostEqual(s2/(N-1), sigmasqrd, places=2,
+                                   msg='%s%r' % (variate.__name__, args))
+
+    def test_constant(self):
+        g = random.Random()
+        N = 100
+        for variate, args, expected in [
+                (g.uniform, (10.0, 10.0), 10.0),
+                (g.triangular, (10.0, 10.0), 10.0),
+                #(g.triangular, (10.0, 10.0, 10.0), 10.0),
+                (g.expovariate, (float('inf'),), 0.0),
+                (g.vonmisesvariate, (3.0, float('inf')), 3.0),
+                (g.gauss, (10.0, 0.0), 10.0),
+                (g.lognormvariate, (0.0, 0.0), 1.0),
+                (g.lognormvariate, (-float('inf'), 0.0), 0.0),
+                (g.normalvariate, (10.0, 0.0), 10.0),
+                (g.paretovariate, (float('inf'),), 1.0),
+                (g.weibullvariate, (10.0, float('inf')), 10.0),
+                (g.weibullvariate, (0.0, 10.0), 0.0),
+            ]:
+            for i in range(N):
+                self.assertEqual(variate(*args), expected)
+
+    def test_von_mises_range(self):
+        # Issue 17149: von mises variates were not consistently in the
+        # range [0, 2*PI].
+        g = random.Random()
+        N = 100
+        for mu in 0.0, 0.1, 3.1, 6.2:
+            for kappa in 0.0, 2.3, 500.0:
+                for _ in range(N):
+                    sample = g.vonmisesvariate(mu, kappa)
+                    self.assertTrue(
+                        0 <= sample <= random.TWOPI,
+                        msg=("vonmisesvariate({}, {}) produced a result {} out"
+                             " of range [0, 2*pi]").format(mu, kappa, sample))
+
+    def test_von_mises_large_kappa(self):
+        # Issue #17141: vonmisesvariate() was hang for large kappas
+        random.vonmisesvariate(0, 1e15)
+        random.vonmisesvariate(0, 1e100)
+
 
 class TestModule(unittest.TestCase):
     def testMagicConstants(self):
diff --git a/lib-python/2.7/test/test_re.py b/lib-python/2.7/test/test_re.py
--- a/lib-python/2.7/test/test_re.py
+++ b/lib-python/2.7/test/test_re.py
@@ -1,4 +1,5 @@
 from test.test_support import verbose, run_unittest, import_module
+from test.test_support import precisionbigmemtest, _2G, cpython_only
 import re
 from re import Scanner
 import sys
@@ -6,6 +7,7 @@
 import traceback
 from weakref import proxy
 
+
 # Misc tests from Tim Peters' re.doc
 
 # WARNING: Don't change details in these tests if you don't know
@@ -174,11 +176,31 @@
         self.assertEqual(re.sub('x*', '-', 'abxd'), '-a-b-d-')
         self.assertEqual(re.sub('x+', '-', 'abxd'), 'ab-d')
 
+    def test_symbolic_groups(self):
+        re.compile('(?P<a>x)(?P=a)(?(a)y)')
+        re.compile('(?P<a1>x)(?P=a1)(?(a1)y)')
+        self.assertRaises(re.error, re.compile, '(?P<a>)(?P<a>)')
+        self.assertRaises(re.error, re.compile, '(?Px)')
+        self.assertRaises(re.error, re.compile, '(?P=)')
+        self.assertRaises(re.error, re.compile, '(?P=1)')
+        self.assertRaises(re.error, re.compile, '(?P=a)')
+        self.assertRaises(re.error, re.compile, '(?P=a1)')
+        self.assertRaises(re.error, re.compile, '(?P=a.)')
+        self.assertRaises(re.error, re.compile, '(?P<)')
+        self.assertRaises(re.error, re.compile, '(?P<>)')
+        self.assertRaises(re.error, re.compile, '(?P<1>)')
+        self.assertRaises(re.error, re.compile, '(?P<a.>)')
+        self.assertRaises(re.error, re.compile, '(?())')
+        self.assertRaises(re.error, re.compile, '(?(a))')
+        self.assertRaises(re.error, re.compile, '(?(1a))')
+        self.assertRaises(re.error, re.compile, '(?(a.))')
+
     def test_symbolic_refs(self):
         self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<a', 'xx')
         self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<', 'xx')
         self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g', 'xx')
         self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<a a>', 'xx')
+        self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<>', 'xx')
         self.assertRaises(re.error, re.sub, '(?P<a>x)', '\g<1a1>', 'xx')
         self.assertRaises(IndexError, re.sub, '(?P<a>x)', '\g<ab>', 'xx')
         self.assertRaises(re.error, re.sub, '(?P<a>x)|(?P<b>y)', '\g<b>', 'xx')
@@ -405,6 +427,12 @@
         self.assertEqual(re.match(u"([\u2222\u2223])",
                                   u"\u2222", re.UNICODE).group(1), u"\u2222")
 
+    def test_big_codesize(self):
+        # Issue #1160
+        r = re.compile('|'.join(('%d'%x for x in range(10000))))
+        self.assertIsNotNone(r.match('1000'))
+        self.assertIsNotNone(r.match('9999'))
+
     def test_anyall(self):
         self.assertEqual(re.match("a.b", "a\nb", re.DOTALL).group(0),
                          "a\nb")
@@ -600,6 +628,15 @@
         self.assertEqual(re.match('(x)*y', 50000*'x'+'y').group(1), 'x')
         self.assertEqual(re.match('(x)*?y', 50000*'x'+'y').group(1), 'x')
 
+    def test_unlimited_zero_width_repeat(self):
+        # Issue #9669
+        self.assertIsNone(re.match(r'(?:a?)*y', 'z'))
+        self.assertIsNone(re.match(r'(?:a?)+y', 'z'))
+        self.assertIsNone(re.match(r'(?:a?){2,}y', 'z'))
+        self.assertIsNone(re.match(r'(?:a?)*?y', 'z'))
+        self.assertIsNone(re.match(r'(?:a?)+?y', 'z'))
+        self.assertIsNone(re.match(r'(?:a?){2,}?y', 'z'))
+
     def test_scanner(self):
         def s_ident(scanner, token): return token
         def s_operator(scanner, token): return "op%s" % token
@@ -793,6 +830,63 @@
         # Test behaviour when not given a string or pattern as parameter
         self.assertRaises(TypeError, re.compile, 0)
 
+    def test_bug_13899(self):
+        # Issue #13899: re pattern r"[\A]" should work like "A" but matches
+        # nothing. Ditto B and Z.
+        self.assertEqual(re.findall(r'[\A\B\b\C\Z]', 'AB\bCZ'),
+                         ['A', 'B', '\b', 'C', 'Z'])
+
+    @precisionbigmemtest(size=_2G, memuse=1)
+    def test_large_search(self, size):
+        # Issue #10182: indices were 32-bit-truncated.
+        s = 'a' * size
+        m = re.search('$', s)
+        self.assertIsNotNone(m)
+        self.assertEqual(m.start(), size)
+        self.assertEqual(m.end(), size)
+
+    # The huge memuse is because of re.sub() using a list and a join()
+    # to create the replacement result.
+    @precisionbigmemtest(size=_2G, memuse=16 + 2)
+    def test_large_subn(self, size):
+        # Issue #10182: indices were 32-bit-truncated.
+        s = 'a' * size
+        r, n = re.subn('', '', s)
+        self.assertEqual(r, s)
+        self.assertEqual(n, size + 1)
+
+
+    def test_repeat_minmax_overflow(self):
+        # Issue #13169
+        string = "x" * 100000
+        self.assertEqual(re.match(r".{65535}", string).span(), (0, 65535))
+        self.assertEqual(re.match(r".{,65535}", string).span(), (0, 65535))
+        self.assertEqual(re.match(r".{65535,}?", string).span(), (0, 65535))
+        self.assertEqual(re.match(r".{65536}", string).span(), (0, 65536))
+        self.assertEqual(re.match(r".{,65536}", string).span(), (0, 65536))
+        self.assertEqual(re.match(r".{65536,}?", string).span(), (0, 65536))
+        # 2**128 should be big enough to overflow both SRE_CODE and Py_ssize_t.
+        self.assertRaises(OverflowError, re.compile, r".{%d}" % 2**128)
+        self.assertRaises(OverflowError, re.compile, r".{,%d}" % 2**128)
+        self.assertRaises(OverflowError, re.compile, r".{%d,}?" % 2**128)
+        self.assertRaises(OverflowError, re.compile, r".{%d,%d}" % (2**129, 2**128))
+
+    @cpython_only
+    def test_repeat_minmax_overflow_maxrepeat(self):
+        try:
+            from _sre import MAXREPEAT
+        except ImportError:
+            self.skipTest('requires _sre.MAXREPEAT constant')
+        string = "x" * 100000
+        self.assertIsNone(re.match(r".{%d}" % (MAXREPEAT - 1), string))
+        self.assertEqual(re.match(r".{,%d}" % (MAXREPEAT - 1), string).span(),
+                         (0, 100000))
+        self.assertIsNone(re.match(r".{%d,}?" % (MAXREPEAT - 1), string))
+        self.assertRaises(OverflowError, re.compile, r".{%d}" % MAXREPEAT)
+        self.assertRaises(OverflowError, re.compile, r".{,%d}" % MAXREPEAT)
+        self.assertRaises(OverflowError, re.compile, r".{%d,}?" % MAXREPEAT)
+
+
 def run_re_tests():
     from test.re_tests import tests, SUCCEED, FAIL, SYNTAX_ERROR
     if verbose:
diff --git a/lib-python/2.7/test/test_readline.py b/lib-python/2.7/test/test_readline.py
--- a/lib-python/2.7/test/test_readline.py
+++ b/lib-python/2.7/test/test_readline.py
@@ -12,6 +12,10 @@
 readline = import_module('readline')
 
 class TestHistoryManipulation (unittest.TestCase):
+
+    @unittest.skipIf(not hasattr(readline, 'clear_history'),
+                     "The history update test cannot be run because the "
+                     "clear_history method is not available.")
     def testHistoryUpdates(self):
         readline.clear_history()
 
diff --git a/lib-python/2.7/test/test_resource.py b/lib-python/2.7/test/test_resource.py
--- a/lib-python/2.7/test/test_resource.py
+++ b/lib-python/2.7/test/test_resource.py
@@ -103,6 +103,23 @@
         except (ValueError, AttributeError):
             pass
 
+    # Issue 6083: Reference counting bug
+    def test_setrusage_refcount(self):
+        try:
+            limits = resource.getrlimit(resource.RLIMIT_CPU)
+        except AttributeError:
+            pass
+        else:
+            class BadSequence:
+                def __len__(self):
+                    return 2
+                def __getitem__(self, key):
+                    if key in (0, 1):
+                        return len(tuple(range(1000000)))
+                    raise IndexError
+
+            resource.setrlimit(resource.RLIMIT_CPU, BadSequence())
+
 def test_main(verbose=None):
     test_support.run_unittest(ResourceTest)
 
diff --git a/lib-python/2.7/test/test_sax.py b/lib-python/2.7/test/test_sax.py
--- a/lib-python/2.7/test/test_sax.py
+++ b/lib-python/2.7/test/test_sax.py
@@ -14,12 +14,28 @@
 from xml.sax.handler import feature_namespaces
 from xml.sax.xmlreader import InputSource, AttributesImpl, AttributesNSImpl
 from cStringIO import StringIO
+import io
+import os.path
+import shutil
+import test.test_support as support
 from test.test_support import findfile, run_unittest
 import unittest
 
 TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata")
 TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata")
 
+supports_unicode_filenames = True
+if not os.path.supports_unicode_filenames:
+    try:
+        support.TESTFN_UNICODE.encode(support.TESTFN_ENCODING)
+    except (AttributeError, UnicodeError, TypeError):
+        # Either the file system encoding is None, or the file name
+        # cannot be encoded in the file system encoding.
+        supports_unicode_filenames = False
+requires_unicode_filenames = unittest.skipUnless(
+        supports_unicode_filenames,
+        'Requires unicode filenames support')
+
 ns_uri = "http://www.python.org/xml-ns/saxtest/"
 
 class XmlTestBase(unittest.TestCase):
@@ -155,9 +171,9 @@
 
 start = '<?xml version="1.0" encoding="iso-8859-1"?>\n'
 
-class XmlgenTest(unittest.TestCase):
+class XmlgenTest:
     def test_xmlgen_basic(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
         gen.startDocument()
         gen.startElement("doc", {})
@@ -167,7 +183,7 @@
         self.assertEqual(result.getvalue(), start + "<doc></doc>")
 
     def test_xmlgen_content(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -179,7 +195,7 @@
         self.assertEqual(result.getvalue(), start + "<doc>huhei</doc>")
 
     def test_xmlgen_pi(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -191,7 +207,7 @@
         self.assertEqual(result.getvalue(), start + "<?test data?><doc></doc>")
 
     def test_xmlgen_content_escape(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -204,7 +220,7 @@
             start + "<doc><huhei&</doc>")
 
     def test_xmlgen_attr_escape(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -223,8 +239,41 @@
              "<e a=\"'"\"></e>"
              "<e a=\"

	\"></e></doc>"))
 
+    def test_xmlgen_encoding(self):
+        encodings = ('iso-8859-15', 'utf-8',
+                     'utf-16be', 'utf-16le',
+                     'utf-32be', 'utf-32le')
+        for encoding in encodings:
+            result = self.ioclass()
+            gen = XMLGenerator(result, encoding=encoding)
+
+            gen.startDocument()
+            gen.startElement("doc", {"a": u'\u20ac'})
+            gen.characters(u"\u20ac")
+            gen.endElement("doc")
+            gen.endDocument()
+
+            self.assertEqual(result.getvalue(), (
+                u'<?xml version="1.0" encoding="%s"?>\n'
+                u'<doc a="\u20ac">\u20ac</doc>' % encoding
+                ).encode(encoding, 'xmlcharrefreplace'))
+
+    def test_xmlgen_unencodable(self):
+        result = self.ioclass()
+        gen = XMLGenerator(result, encoding='ascii')
+
+        gen.startDocument()
+        gen.startElement("doc", {"a": u'\u20ac'})
+        gen.characters(u"\u20ac")
+        gen.endElement("doc")
+        gen.endDocument()
+
+        self.assertEqual(result.getvalue(),
+                '<?xml version="1.0" encoding="ascii"?>\n'
+                '<doc a="€">€</doc>')
+
     def test_xmlgen_ignorable(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -236,7 +285,7 @@
         self.assertEqual(result.getvalue(), start + "<doc> </doc>")
 
     def test_xmlgen_ns(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -254,7 +303,7 @@
                                          ns_uri))
 
     def test_1463026_1(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -265,7 +314,7 @@
         self.assertEqual(result.getvalue(), start+'<a b="c"></a>')
 
     def test_1463026_2(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -278,7 +327,7 @@
         self.assertEqual(result.getvalue(), start+'<a xmlns="qux"></a>')
 
     def test_1463026_3(self):
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -294,7 +343,7 @@
     def test_5027_1(self):
         # The xml prefix (as in xml:lang below) is reserved and bound by
         # definition to http://www.w3.org/XML/1998/namespace.  XMLGenerator had
-        # a bug whereby a KeyError is thrown because this namespace is missing
+        # a bug whereby a KeyError is raised because this namespace is missing
         # from a dictionary.
         #
         # This test demonstrates the bug by parsing a document.
@@ -306,7 +355,7 @@
 
         parser = make_parser()
         parser.setFeature(feature_namespaces, True)
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
         parser.setContentHandler(gen)
         parser.parse(test_xml)
@@ -320,12 +369,12 @@
     def test_5027_2(self):
         # The xml prefix (as in xml:lang below) is reserved and bound by
         # definition to http://www.w3.org/XML/1998/namespace.  XMLGenerator had
-        # a bug whereby a KeyError is thrown because this namespace is missing
+        # a bug whereby a KeyError is raised because this namespace is missing
         # from a dictionary.
         #
         # This test demonstrates the bug by direct manipulation of the
         # XMLGenerator.
-        result = StringIO()
+        result = self.ioclass()
         gen = XMLGenerator(result)
 
         gen.startDocument()
@@ -345,6 +394,44 @@
                           '<a:g2 xml:lang="en">Hello</a:g2>'
                          '</a:g1>'))
 
+    def test_no_close_file(self):
+        result = self.ioclass()
+        def func(out):
+            gen = XMLGenerator(out)
+            gen.startDocument()
+            gen.startElement("doc", {})
+        func(result)
+        self.assertFalse(result.closed)
+
+    def test_xmlgen_fragment(self):
+        result = self.ioclass()
+        gen = XMLGenerator(result)
+
+        # Don't call gen.startDocument()
+        gen.startElement("foo", {"a": "1.0"})
+        gen.characters("Hello")
+        gen.endElement("foo")
+        gen.startElement("bar", {"b": "2.0"})
+        gen.endElement("bar")
+        # Don't call gen.endDocument()
+
+        self.assertEqual(result.getvalue(),
+                         '<foo a="1.0">Hello</foo><bar b="2.0"></bar>')
+
+class StringXmlgenTest(XmlgenTest, unittest.TestCase):
+    ioclass = StringIO
+
+class BytesIOXmlgenTest(XmlgenTest, unittest.TestCase):
+    ioclass = io.BytesIO
+
+class WriterXmlgenTest(XmlgenTest, unittest.TestCase):
+    class ioclass(list):
+        write = list.append
+        closed = False
+
+        def getvalue(self):
+            return b''.join(self)
+
 
 class XMLFilterBaseTest(unittest.TestCase):
     def test_filter_basic(self):
@@ -384,6 +471,21 @@
 
         self.assertEqual(result.getvalue(), xml_test_out)
 
+    @requires_unicode_filenames
+    def test_expat_file_unicode(self):
+        fname = support.TESTFN_UNICODE
+        shutil.copyfile(TEST_XMLFILE, fname)
+        self.addCleanup(support.unlink, fname)
+
+        parser = create_parser()
+        result = StringIO()
+        xmlgen = XMLGenerator(result)
+
+        parser.setContentHandler(xmlgen)
+        parser.parse(open(fname))
+
+        self.assertEqual(result.getvalue(), xml_test_out)
+
     # ===== DTDHandler support
 
     class TestDTDHandler:
@@ -523,6 +625,21 @@
 
         self.assertEqual(result.getvalue(), xml_test_out)
 
+    @requires_unicode_filenames
+    def test_expat_inpsource_sysid_unicode(self):
+        fname = support.TESTFN_UNICODE
+        shutil.copyfile(TEST_XMLFILE, fname)
+        self.addCleanup(support.unlink, fname)
+
+        parser = create_parser()
+        result = StringIO()
+        xmlgen = XMLGenerator(result)
+
+        parser.setContentHandler(xmlgen)
+        parser.parse(InputSource(fname))
+
+        self.assertEqual(result.getvalue(), xml_test_out)
+
     def test_expat_inpsource_stream(self):
         parser = create_parser()
         result = StringIO()
@@ -596,6 +713,21 @@
         self.assertEqual(parser.getSystemId(), TEST_XMLFILE)
         self.assertEqual(parser.getPublicId(), None)
 
+    @requires_unicode_filenames
+    def test_expat_locator_withinfo_unicode(self):
+        fname = support.TESTFN_UNICODE
+        shutil.copyfile(TEST_XMLFILE, fname)
+        self.addCleanup(support.unlink, fname)
+
+        result = StringIO()
+        xmlgen = XMLGenerator(result)
+        parser = create_parser()
+        parser.setContentHandler(xmlgen)
+        parser.parse(fname)
+
+        self.assertEqual(parser.getSystemId(), fname)
+        self.assertEqual(parser.getPublicId(), None)
+
 
 # ===========================================================================
 #
@@ -744,7 +876,9 @@
 def test_main():
     run_unittest(MakeParserTest,
                  SaxutilsTest,
-                 XmlgenTest,
+                 StringXmlgenTest,
+                 BytesIOXmlgenTest,
+                 WriterXmlgenTest,
                  ExpatReaderTest,
                  ErrorReportingTest,
                  XmlReaderTest)
diff --git a/lib-python/2.7/test/test_select.py b/lib-python/2.7/test/test_select.py
--- a/lib-python/2.7/test/test_select.py
+++ b/lib-python/2.7/test/test_select.py
@@ -49,6 +49,15 @@
             self.fail('Unexpected return values from select():', rfd, wfd, xfd)
         p.close()
 
+    # Issue 16230: Crash on select resized list
+    def test_select_mutated(self):
+        a = []
+        class F:
+            def fileno(self):
+                del a[-1]
+                return sys.__stdout__.fileno()
+        a[:] = [F()] * 10
+        self.assertEqual(select.select([], a, []), ([], a[:5], []))
 
 def test_main():
     test_support.run_unittest(SelectTestCase)
diff --git a/lib-python/2.7/test/test_shutil.py b/lib-python/2.7/test/test_shutil.py
--- a/lib-python/2.7/test/test_shutil.py
+++ b/lib-python/2.7/test/test_shutil.py
@@ -7,6 +7,7 @@
 import stat
 import os
 import os.path
+import errno
 from os.path import splitdrive
 from distutils.spawn import find_executable, spawn
 from shutil import (_make_tarball, _make_zipfile, make_archive,
@@ -339,6 +340,35 @@
                 shutil.rmtree(TESTFN, ignore_errors=True)
                 shutil.rmtree(TESTFN2, ignore_errors=True)
 
+    @unittest.skipUnless(hasattr(os, 'chflags') and
+                         hasattr(errno, 'EOPNOTSUPP') and
+                         hasattr(errno, 'ENOTSUP'),
+                         "requires os.chflags, EOPNOTSUPP & ENOTSUP")
+    def test_copystat_handles_harmless_chflags_errors(self):
+        tmpdir = self.mkdtemp()
+        file1 = os.path.join(tmpdir, 'file1')
+        file2 = os.path.join(tmpdir, 'file2')
+        self.write_file(file1, 'xxx')
+        self.write_file(file2, 'xxx')
+
+        def make_chflags_raiser(err):
+            ex = OSError()
+
+            def _chflags_raiser(path, flags):
+                ex.errno = err
+                raise ex
+            return _chflags_raiser
+        old_chflags = os.chflags
+        try:
+            for err in errno.EOPNOTSUPP, errno.ENOTSUP:
+                os.chflags = make_chflags_raiser(err)
+                shutil.copystat(file1, file2)
+            # assert others errors break it
+            os.chflags = make_chflags_raiser(errno.EOPNOTSUPP + errno.ENOTSUP)
+            self.assertRaises(OSError, shutil.copystat, file1, file2)
+        finally:
+            os.chflags = old_chflags
+
     @unittest.skipUnless(zlib, "requires zlib")
     def test_make_tarball(self):
         # creating something to tar
diff --git a/lib-python/2.7/test/test_signal.py b/lib-python/2.7/test/test_signal.py
--- a/lib-python/2.7/test/test_signal.py
+++ b/lib-python/2.7/test/test_signal.py
@@ -109,7 +109,7 @@
             # This wait should be interrupted by the signal's exception.
             self.wait(child)
             time.sleep(1)  # Give the signal time to be delivered.
-            self.fail('HandlerBCalled exception not thrown')
+            self.fail('HandlerBCalled exception not raised')
         except HandlerBCalled:
             self.assertTrue(self.b_called)
             self.assertFalse(self.a_called)
@@ -148,7 +148,7 @@
         # test-running process from all the signals. It then
         # communicates with that child process over a pipe and
         # re-raises information about any exceptions the child
-        # throws. The real work happens in self.run_test().
+        # raises. The real work happens in self.run_test().
         os_done_r, os_done_w = os.pipe()
         with closing(os.fdopen(os_done_r)) as done_r, \
              closing(os.fdopen(os_done_w, 'w')) as done_w:
@@ -227,6 +227,13 @@
             signal.signal(7, handler)
 
 
+class WakeupFDTests(unittest.TestCase):
+
+    def test_invalid_fd(self):
+        fd = test_support.make_bad_fd()
+        self.assertRaises(ValueError, signal.set_wakeup_fd, fd)
+
+
 @unittest.skipIf(sys.platform == "win32", "Not valid on Windows")
 class WakeupSignalTests(unittest.TestCase):
     TIMEOUT_FULL = 10
@@ -485,8 +492,9 @@
 
 def test_main():
     test_support.run_unittest(BasicSignalTests, InterProcessSignalTests,
-                              WakeupSignalTests, SiginterruptTest,
-                              ItimerTest, WindowsSignalTests)
+                              WakeupFDTests, WakeupSignalTests,
+                              SiginterruptTest, ItimerTest,
+                              WindowsSignalTests)
 
 
 if __name__ == "__main__":
diff --git a/lib-python/2.7/test/test_socket.py b/lib-python/2.7/test/test_socket.py
--- a/lib-python/2.7/test/test_socket.py
+++ b/lib-python/2.7/test/test_socket.py
@@ -6,6 +6,7 @@
 import errno
 import socket
 import select
+import _testcapi
 import time
 import traceback
 import Queue
@@ -644,9 +645,10 @@
         if SUPPORTS_IPV6:
             socket.getaddrinfo('::1', 80)
         # port can be a string service name such as "http", a numeric
-        # port number or None
+        # port number (int or long), or None
         socket.getaddrinfo(HOST, "http")
         socket.getaddrinfo(HOST, 80)
+        socket.getaddrinfo(HOST, 80L)
         socket.getaddrinfo(HOST, None)
         # test family and socktype filters
         infos = socket.getaddrinfo(HOST, None, socket.AF_INET)
@@ -699,11 +701,17 @@
     def test_sendall_interrupted_with_timeout(self):
         self.check_sendall_interrupted(True)
 
-    def testListenBacklog0(self):
+    def test_listen_backlog(self):
+        for backlog in 0, -1:
+            srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            srv.bind((HOST, 0))
+            srv.listen(backlog)
+            srv.close()
+
+        # Issue 15989
         srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         srv.bind((HOST, 0))
-        # backlog = 0
-        srv.listen(0)
+        self.assertRaises(OverflowError, srv.listen, _testcapi.INT_MAX + 1)
         srv.close()
 
     @unittest.skipUnless(SUPPORTS_IPV6, 'IPv6 required for this test.')
@@ -807,6 +815,11 @@
 
     def _testShutdown(self):
         self.serv_conn.send(MSG)
+        # Issue 15989
+        self.assertRaises(OverflowError, self.serv_conn.shutdown,
+                          _testcapi.INT_MAX + 1)
+        self.assertRaises(OverflowError, self.serv_conn.shutdown,
+                          2 + (_testcapi.UINT_MAX + 1))
         self.serv_conn.shutdown(2)
 
 @unittest.skipUnless(thread, 'Threading required for this test.')
@@ -882,7 +895,10 @@
 
     def testSetBlocking(self):
         # Testing whether set blocking works
-        self.serv.setblocking(0)
+        self.serv.setblocking(True)
+        self.assertIsNone(self.serv.gettimeout())
+        self.serv.setblocking(False)
+        self.assertEqual(self.serv.gettimeout(), 0.0)
         start = time.time()
         try:
             self.serv.accept()
@@ -890,6 +906,10 @@
             pass
         end = time.time()
         self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.")
+        # Issue 15989
+        if _testcapi.UINT_MAX < _testcapi.ULONG_MAX:
+            self.serv.setblocking(_testcapi.UINT_MAX + 1)
+            self.assertIsNone(self.serv.gettimeout())
 
     def _testSetBlocking(self):
         pass
@@ -961,8 +981,8 @@
     def tearDown(self):
         self.serv_file.close()
         self.assertTrue(self.serv_file.closed)
+        SocketConnectedTest.tearDown(self)
         self.serv_file = None
-        SocketConnectedTest.tearDown(self)
 
     def clientSetUp(self):
         SocketConnectedTest.clientSetUp(self)
@@ -1150,6 +1170,64 @@
 
     bufsize = 1 # Default-buffered for reading; line-buffered for writing
 
+    class SocketMemo(object):
+        """A wrapper to keep track of sent data, needed to examine write behaviour"""
+        def __init__(self, sock):
+            self._sock = sock
+            self.sent = []
+
+        def send(self, data, flags=0):
+            n = self._sock.send(data, flags)
+            self.sent.append(data[:n])
+            return n
+
+        def sendall(self, data, flags=0):
+            self._sock.sendall(data, flags)
+            self.sent.append(data)
+
+        def __getattr__(self, attr):
+            return getattr(self._sock, attr)
+
+        def getsent(self):
+            return [e.tobytes() if isinstance(e, memoryview) else e for e in self.sent]
+
+    def setUp(self):
+        FileObjectClassTestCase.setUp(self)
+        self.serv_file._sock = self.SocketMemo(self.serv_file._sock)
+
+    def testLinebufferedWrite(self):
+        # Write two lines, in small chunks
+        msg = MSG.strip()
+        print >> self.serv_file, msg,
+        print >> self.serv_file, msg
+
+        # second line:
+        print >> self.serv_file, msg,
+        print >> self.serv_file, msg,
+        print >> self.serv_file, msg
+
+        # third line
+        print >> self.serv_file, ''
+
+        self.serv_file.flush()
+
+        msg1 = "%s %s\n"%(msg, msg)
+        msg2 =  "%s %s %s\n"%(msg, msg, msg)
+        msg3 =  "\n"
+        self.assertEqual(self.serv_file._sock.getsent(), [msg1, msg2, msg3])
+
+    def _testLinebufferedWrite(self):
+        msg = MSG.strip()
+        msg1 = "%s %s\n"%(msg, msg)
+        msg2 =  "%s %s %s\n"%(msg, msg, msg)
+        msg3 =  "\n"
+        l1 = self.cli_file.readline()
+        self.assertEqual(l1, msg1)
+        l2 = self.cli_file.readline()
+        self.assertEqual(l2, msg2)
+        l3 = self.cli_file.readline()
+        self.assertEqual(l3, msg3)
+
 
 class SmallBufferedFileObjectClassTestCase(FileObjectClassTestCase):
 
@@ -1197,7 +1275,26 @@
         port = test_support.find_unused_port()
         with self.assertRaises(socket.error) as cm:
             socket.create_connection((HOST, port))
-        self.assertEqual(cm.exception.errno, errno.ECONNREFUSED)
+
+        # Issue #16257: create_connection() calls getaddrinfo() against
+        # 'localhost'.  This may result in an IPV6 addr being returned
+        # as well as an IPV4 one:
+        #   >>> socket.getaddrinfo('localhost', port, 0, SOCK_STREAM)
+        #   >>> [(2,  2, 0, '', ('127.0.0.1', 41230)),
+        #        (26, 2, 0, '', ('::1', 41230, 0, 0))]
+        #
+        # create_connection() enumerates through all the addresses returned
+        # and if it doesn't successfully bind to any of them, it propagates
+        # the last exception it encountered.
+        #
+        # On Solaris, ENETUNREACH is returned in this circumstance instead
+        # of ECONNREFUSED.  So, if that errno exists, add it to our list of
+        # expected errnos.
+        expected_errnos = [ errno.ECONNREFUSED, ]
+        if hasattr(errno, 'ENETUNREACH'):
+            expected_errnos.append(errno.ENETUNREACH)
+
+        self.assertIn(cm.exception.errno, expected_errnos)
 
     def test_create_connection_timeout(self):
         # Issue #9792: create_connection() should not recast timeout errors
diff --git a/lib-python/2.7/test/test_socketserver.py b/lib-python/2.7/test/test_socketserver.py
--- a/lib-python/2.7/test/test_socketserver.py
+++ b/lib-python/2.7/test/test_socketserver.py
@@ -8,6 +8,8 @@
 import select
 import signal
 import socket
+import select
+import errno
 import tempfile
 import unittest
 import SocketServer
@@ -32,8 +34,11 @@
     if hasattr(signal, 'alarm'):
         signal.alarm(n)
 
+# Remember real select() to avoid interferences with mocking
+_real_select = select.select
+
 def receive(sock, n, timeout=20):
-    r, w, x = select.select([sock], [], [], timeout)
+    r, w, x = _real_select([sock], [], [], timeout)
     if sock in r:
         return sock.recv(n)
     else:
@@ -53,7 +58,7 @@
 def simple_subprocess(testcase):
     pid = os.fork()
     if pid == 0:
-        # Don't throw an exception; it would be caught by the test harness.
+        # Don't raise an exception; it would be caught by the test harness.
         os._exit(72)
     yield None
     pid2, status = os.waitpid(pid, 0)
@@ -225,6 +230,38 @@
                                 SocketServer.DatagramRequestHandler,
                                 self.dgram_examine)
 
+    @contextlib.contextmanager
+    def mocked_select_module(self):
+        """Mocks the select.select() call to raise EINTR for first call"""
+        old_select = select.select
+
+        class MockSelect:
+            def __init__(self):
+                self.called = 0
+
+            def __call__(self, *args):
+                self.called += 1
+                if self.called == 1:
+                    # raise the exception on first call
+                    raise select.error(errno.EINTR, os.strerror(errno.EINTR))
+                else:
+                    # Return real select value for consecutive calls
+                    return old_select(*args)
+
+        select.select = MockSelect()
+        try:
+            yield select.select
+        finally:
+            select.select = old_select
+
+    def test_InterruptServerSelectCall(self):
+        with self.mocked_select_module() as mock_select:
+            pid = self.run_server(SocketServer.TCPServer,
+                                  SocketServer.StreamRequestHandler,
+                                  self.stream_examine)
+            # Make sure select was called again:
+            self.assertGreater(mock_select.called, 1)
+
     # Alas, on Linux (at least) recvfrom() doesn't return a meaningful
     # client address so this cannot work:
 
diff --git a/lib-python/2.7/test/test_ssl.py b/lib-python/2.7/test/test_ssl.py
--- a/lib-python/2.7/test/test_ssl.py
+++ b/lib-python/2.7/test/test_ssl.py
@@ -95,12 +95,8 @@
             sys.stdout.write("\n RAND_status is %d (%s)\n"
                              % (v, (v and "sufficient randomness") or
                                 "insufficient randomness"))
-        try:
-            ssl.RAND_egd(1)
-        except TypeError:
-            pass
-        else:
-            print "didn't raise TypeError"
+        self.assertRaises(TypeError, ssl.RAND_egd, 1)
+        self.assertRaises(TypeError, ssl.RAND_egd, 'foo', 1)
         ssl.RAND_add("this is a random string", 75.0)
 
     def test_parse_cert(self):
@@ -111,13 +107,12 @@
         if test_support.verbose:
             sys.stdout.write("\n" + pprint.pformat(p) + "\n")
         self.assertEqual(p['subject'],
-                         ((('countryName', u'US'),),
-                          (('stateOrProvinceName', u'Delaware'),),
-                          (('localityName', u'Wilmington'),),
-                          (('organizationName', u'Python Software Foundation'),),
-                          (('organizationalUnitName', u'SSL'),),
-                          (('commonName', u'somemachine.python.org'),)),
+                         ((('countryName', 'XY'),),
+                          (('localityName', 'Castle Anthrax'),),
+                          (('organizationName', 'Python Software Foundation'),),
+                          (('commonName', 'localhost'),))
                         )
+        self.assertEqual(p['subjectAltName'], (('DNS', 'localhost'),))
         # Issue #13034: the subjectAltName in some certificates
         # (notably projects.developer.nokia.com:443) wasn't parsed
         p = ssl._ssl._test_decode_cert(NOKIACERT)
@@ -284,6 +279,34 @@
             finally:
                 s.close()
 
+    def test_timeout_connect_ex(self):
+        # Issue #12065: on a timeout, connect_ex() should return the original
+        # errno (mimicking the behaviour of non-SSL sockets).
+        with test_support.transient_internet("svn.python.org"):
+            s = ssl.wrap_socket(socket.socket(socket.AF_INET),
+                                cert_reqs=ssl.CERT_REQUIRED,
+                                ca_certs=SVN_PYTHON_ORG_ROOT_CERT,
+                                do_handshake_on_connect=False)
+            try:
+                s.settimeout(0.0000001)
+                rc = s.connect_ex(('svn.python.org', 443))
+                if rc == 0:
+                    self.skipTest("svn.python.org responded too quickly")
+                self.assertIn(rc, (errno.EAGAIN, errno.EWOULDBLOCK))
+            finally:
+                s.close()
+
+    def test_connect_ex_error(self):
+        with test_support.transient_internet("svn.python.org"):
+            s = ssl.wrap_socket(socket.socket(socket.AF_INET),
+                                cert_reqs=ssl.CERT_REQUIRED,
+                                ca_certs=SVN_PYTHON_ORG_ROOT_CERT)
+            try:
+                self.assertEqual(errno.ECONNREFUSED,
+                                 s.connect_ex(("svn.python.org", 444)))
+            finally:
+                s.close()
+
     @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows")
     def test_makefile_close(self):
         # Issue #5238: creating a file-like object with makefile() shouldn't
@@ -355,7 +378,8 @@
         # SHA256 was added in OpenSSL 0.9.8
         if ssl.OPENSSL_VERSION_INFO < (0, 9, 8, 0, 15):
             self.skipTest("SHA256 not available on %r" % ssl.OPENSSL_VERSION)
-        # NOTE: https://sha256.tbs-internet.com is another possible test host
+        self.skipTest("remote host needs SNI, only available on Python 3.2+")
+        # NOTE: https://sha2.hboeck.de is another possible test host
         remote = ("sha256.tbs-internet.com", 443)
         sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem")
         with test_support.transient_internet("sha256.tbs-internet.com"):
diff --git a/lib-python/2.7/test/test_str.py b/lib-python/2.7/test/test_str.py
--- a/lib-python/2.7/test/test_str.py
+++ b/lib-python/2.7/test/test_str.py
@@ -35,6 +35,18 @@
         string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
         self.assertRaises(OverflowError, '%c'.__mod__, 0x1234)
 
+    @test_support.cpython_only
+    def test_formatting_huge_precision(self):
+        from _testcapi import INT_MAX
+        format_string = "%.{}f".format(INT_MAX + 1)
+        with self.assertRaises(ValueError):
+            result = format_string % 2.34
+
+    def test_formatting_huge_width(self):
+        format_string = "%{}f".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format_string % 2.34
+
     def test_conversion(self):
         # Make sure __str__() behaves properly
         class Foo0:
@@ -371,6 +383,21 @@
         self.assertRaises(ValueError, format, "", "-")
         self.assertRaises(ValueError, "{0:=s}".format, '')
 
+    def test_format_huge_precision(self):
+        format_string = ".{}f".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format(2.34, format_string)
+
+    def test_format_huge_width(self):
+        format_string = "{}f".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format(2.34, format_string)
+
+    def test_format_huge_item_number(self):
+        format_string = "{{{}:.6f}}".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format_string.format(2.34)
+
     def test_format_auto_numbering(self):
         class C:
             def __init__(self, x=100):
diff --git a/lib-python/2.7/test/test_strptime.py b/lib-python/2.7/test/test_strptime.py
--- a/lib-python/2.7/test/test_strptime.py
+++ b/lib-python/2.7/test/test_strptime.py
@@ -378,6 +378,14 @@
         need_escaping = ".^$*+?{}\[]|)("
         self.assertTrue(_strptime._strptime_time(need_escaping, need_escaping))
 
+    def test_feb29_on_leap_year_without_year(self):
+        time.strptime("Feb 29", "%b %d")
+
+    def test_mar1_comes_after_feb29_even_when_omitting_the_year(self):
+        self.assertLess(
+                time.strptime("Feb 29", "%b %d"),
+                time.strptime("Mar 1", "%b %d"))
+
 class Strptime12AMPMTests(unittest.TestCase):
     """Test a _strptime regression in '%I %p' at 12 noon (12 PM)"""
 
diff --git a/lib-python/2.7/test/test_struct.py b/lib-python/2.7/test/test_struct.py
--- a/lib-python/2.7/test/test_struct.py
+++ b/lib-python/2.7/test/test_struct.py
@@ -3,7 +3,8 @@
 import unittest
 import struct
 import inspect
-from test.test_support import run_unittest, check_warnings, check_py3k_warnings
+from test import test_support as support
+from test.test_support import (check_warnings, check_py3k_warnings)
 
 import sys
 ISBIGENDIAN = sys.byteorder == "big"
@@ -544,8 +545,29 @@
         hugecount2 = '{}b{}H'.format(sys.maxsize//2, sys.maxsize//2)
         self.assertRaises(struct.error, struct.calcsize, hugecount2)
 
+    def check_sizeof(self, format_str, number_of_codes):
+        # The size of 'PyStructObject'
+        totalsize = support.calcobjsize('5P')
+        # The size taken up by the 'formatcode' dynamic array
+        totalsize += struct.calcsize('3P') * (number_of_codes + 1)
+        support.check_sizeof(self, struct.Struct(format_str), totalsize)
+
+    @support.cpython_only
+    def test__sizeof__(self):
+        for code in integer_codes:
+            self.check_sizeof(code, 1)
+        self.check_sizeof('BHILfdspP', 9)
+        self.check_sizeof('B' * 1234, 1234)
+        self.check_sizeof('fd', 2)
+        self.check_sizeof('xxxxxxxxxxxxxx', 0)
+        self.check_sizeof('100H', 100)
+        self.check_sizeof('187s', 1)
+        self.check_sizeof('20p', 1)
+        self.check_sizeof('0s', 1)
+        self.check_sizeof('0c', 0)
+
 def test_main():
-    run_unittest(StructTest)
+    support.run_unittest(StructTest)
 
 if __name__ == '__main__':
     test_main()
diff --git a/lib-python/2.7/test/test_subprocess.py b/lib-python/2.7/test/test_subprocess.py
--- a/lib-python/2.7/test/test_subprocess.py
+++ b/lib-python/2.7/test/test_subprocess.py
@@ -58,6 +58,18 @@
         self.assertEqual(actual, expected, msg)
 
 
+class PopenTestException(Exception):
+    pass
+
+
+class PopenExecuteChildRaises(subprocess.Popen):
+    """Popen subclass for testing cleanup of subprocess.PIPE filehandles when
+    _execute_child fails.
+    """
+    def _execute_child(self, *args, **kwargs):
+        raise PopenTestException("Forced Exception for Test")
+
+
 class ProcessTestCase(BaseTestCase):
 
     def test_call_seq(self):
@@ -526,6 +538,7 @@
         finally:
             for h in handles:
                 os.close(h)
+            test_support.unlink(test_support.TESTFN)
 
     def test_list2cmdline(self):
         self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']),
@@ -631,6 +644,27 @@
         time.sleep(2)
         p.communicate("x" * 2**20)
 
+    # This test is Linux-ish specific for simplicity to at least have
+    # some coverage.  It is not a platform specific bug.
+    @unittest.skipUnless(os.path.isdir('/proc/%d/fd' % os.getpid()),
+                         "Linux specific")
+    def test_failed_child_execute_fd_leak(self):
+        """Test for the fork() failure fd leak reported in issue16327."""
+        fd_directory = '/proc/%d/fd' % os.getpid()
+        fds_before_popen = os.listdir(fd_directory)
+        with self.assertRaises(PopenTestException):
+            PopenExecuteChildRaises(
+                    [sys.executable, '-c', 'pass'], stdin=subprocess.PIPE,
+                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+        # NOTE: This test doesn't verify that the real _execute_child
+        # does not close the file descriptors itself on the way out
+        # during an exception.  Code inspection has confirmed that.
+
+        fds_after_exception = os.listdir(fd_directory)
+        self.assertEqual(fds_before_popen, fds_after_exception)
+
+
 # context manager
 class _SuppressCoreFiles(object):
     """Try to prevent core files from being created."""
@@ -717,6 +751,52 @@
         self.addCleanup(p.stdout.close)
         self.assertEqual(p.stdout.read(), "apple")
 
+    class _TestExecuteChildPopen(subprocess.Popen):
+        """Used to test behavior at the end of _execute_child."""
+        def __init__(self, testcase, *args, **kwargs):
+            self._testcase = testcase
+            subprocess.Popen.__init__(self, *args, **kwargs)
+
+        def _execute_child(
+                self, args, executable, preexec_fn, close_fds, cwd, env,
+                universal_newlines, startupinfo, creationflags, shell,
+                p2cread, p2cwrite,
+                c2pread, c2pwrite,
+                errread, errwrite):
+            try:
+                subprocess.Popen._execute_child(
+                        self, args, executable, preexec_fn, close_fds,
+                        cwd, env, universal_newlines,
+                        startupinfo, creationflags, shell,
+                        p2cread, p2cwrite,
+                        c2pread, c2pwrite,
+                        errread, errwrite)
+            finally:
+                # Open a bunch of file descriptors and verify that
+                # none of them are the same as the ones the Popen
+                # instance is using for stdin/stdout/stderr.
+                devzero_fds = [os.open("/dev/zero", os.O_RDONLY)
+                               for _ in range(8)]
+                try:
+                    for fd in devzero_fds:
+                        self._testcase.assertNotIn(
+                                fd, (p2cwrite, c2pread, errread))
+                finally:
+                    map(os.close, devzero_fds)
+
+    @unittest.skipIf(not os.path.exists("/dev/zero"), "/dev/zero required.")
+    def test_preexec_errpipe_does_not_double_close_pipes(self):
+        """Issue16140: Don't double close pipes on preexec error."""
+
+        def raise_it():
+            raise RuntimeError("force the _execute_child() errpipe_data path.")
+
+        with self.assertRaises(RuntimeError):
+            self._TestExecuteChildPopen(
+                    self, [sys.executable, "-c", "pass"],
+                    stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                    stderr=subprocess.PIPE, preexec_fn=raise_it)
+
     def test_args_string(self):
         # args is a string
         f, fname = mkstemp()
@@ -812,6 +892,8 @@
         getattr(p, method)(*args)
         return p
 
+    @unittest.skipIf(sys.platform.startswith(('netbsd', 'openbsd')),
+                     "Due to known OS bug (issue #16762)")
     def _kill_dead_process(self, method, *args):
         # Do not inherit file handles from the parent.
         # It should fix failures on some platforms.
diff --git a/lib-python/2.7/test/test_support.py b/lib-python/2.7/test/test_support.py
--- a/lib-python/2.7/test/test_support.py
+++ b/lib-python/2.7/test/test_support.py
@@ -18,6 +18,9 @@
 import UserDict
 import re
 import time
+import struct
+import _testcapi
+import sysconfig
 try:
     import thread
 except ImportError:
@@ -179,15 +182,79 @@
     except KeyError:
         pass
 
+if sys.platform.startswith("win"):
+    def _waitfor(func, pathname, waitall=False):
+        # Peform the operation
+        func(pathname)
+        # Now setup the wait loop
+        if waitall:
+            dirname = pathname
+        else:
+            dirname, name = os.path.split(pathname)
+            dirname = dirname or '.'
+        # Check for `pathname` to be removed from the filesystem.
+        # The exponential backoff of the timeout amounts to a total
+        # of ~1 second after which the deletion is probably an error
+        # anyway.
+        # Testing on a i7 at 4.3GHz shows that usually only 1 iteration is
+        # required when contention occurs.
+        timeout = 0.001
+        while timeout < 1.0:
+            # Note we are only testing for the existance of the file(s) in
+            # the contents of the directory regardless of any security or
+            # access rights.  If we have made it this far, we have sufficient
+            # permissions to do that much using Python's equivalent of the
+            # Windows API FindFirstFile.
+            # Other Windows APIs can fail or give incorrect results when
+            # dealing with files that are pending deletion.
+            L = os.listdir(dirname)
+            if not (L if waitall else name in L):
+                return
+            # Increase the timeout and try again
+            time.sleep(timeout)
+            timeout *= 2
+        warnings.warn('tests may fail, delete still pending for ' + pathname,
+                      RuntimeWarning, stacklevel=4)
+
+    def _unlink(filename):
+        _waitfor(os.unlink, filename)
+
+    def _rmdir(dirname):
+        _waitfor(os.rmdir, dirname)
+
+    def _rmtree(path):
+        def _rmtree_inner(path):
+            for name in os.listdir(path):
+                fullname = os.path.join(path, name)
+                if os.path.isdir(fullname):
+                    _waitfor(_rmtree_inner, fullname, waitall=True)
+                    os.rmdir(fullname)
+                else:
+                    os.unlink(fullname)
+        _waitfor(_rmtree_inner, path, waitall=True)
+        _waitfor(os.rmdir, path)
+else:
+    _unlink = os.unlink
+    _rmdir = os.rmdir
+    _rmtree = shutil.rmtree
+
 def unlink(filename):
     try:
-        os.unlink(filename)
+        _unlink(filename)
     except OSError:
         pass
 
+def rmdir(dirname):
+    try:
+        _rmdir(dirname)
+    except OSError as error:
+        # The directory need not exist.
+        if error.errno != errno.ENOENT:
+            raise
+
 def rmtree(path):
     try:
-        shutil.rmtree(path)
+        _rmtree(path)
     except OSError, e:
         # Unix returns ENOENT, Windows returns ESRCH.
         if e.errno not in (errno.ENOENT, errno.ESRCH):
@@ -405,7 +472,7 @@
     the CWD, an error is raised.  If it's True, only a warning is raised
     and the original CWD is used.
     """
-    if isinstance(name, unicode):
+    if have_unicode and isinstance(name, unicode):
         try:
             name = name.encode(sys.getfilesystemencoding() or 'ascii')
         except UnicodeEncodeError:
@@ -767,6 +834,9 @@
         ('EAI_FAIL', -4),
         ('EAI_NONAME', -2),
         ('EAI_NODATA', -5),
+        # Windows defines EAI_NODATA as 11001 but idiotic getaddrinfo()
+        # implementation actually returns WSANO_DATA i.e. 11004.
+        ('WSANO_DATA', 11004),
     ]
 
     denied = ResourceDenied("Resource '%s' is not available" % resource_name)
@@ -858,6 +928,32 @@
     gc.collect()
 
 
+_header = '2P'
+if hasattr(sys, "gettotalrefcount"):
+    _header = '2P' + _header
+_vheader = _header + 'P'
+
+def calcobjsize(fmt):
+    return struct.calcsize(_header + fmt + '0P')
+
+def calcvobjsize(fmt):
+    return struct.calcsize(_vheader + fmt + '0P')
+
+
+_TPFLAGS_HAVE_GC = 1<<14
+_TPFLAGS_HEAPTYPE = 1<<9
+
+def check_sizeof(test, o, size):
+    result = sys.getsizeof(o)
+    # add GC header size
+    if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\
+        ((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))):
+        size += _testcapi.SIZEOF_PYGC_HEAD
+    msg = 'wrong size for %s: got %d, expected %d' \
+            % (type(o), result, size)
+    test.assertEqual(result, size, msg)
+
+
 #=======================================================================
 # Decorator for running a function in a different locale, correctly resetting
 # it afterwards.
@@ -966,7 +1062,7 @@
         return wrapper
     return decorator
 
-def precisionbigmemtest(size, memuse, overhead=5*_1M):
+def precisionbigmemtest(size, memuse, overhead=5*_1M, dry_run=True):
     def decorator(f):
         def wrapper(self):
             if not real_max_memuse:
@@ -974,11 +1070,12 @@
             else:
                 maxsize = size
 
-                if real_max_memuse and real_max_memuse < maxsize * memuse:
-                    if verbose:
-                        sys.stderr.write("Skipping %s because of memory "
-                                         "constraint\n" % (f.__name__,))
-                    return
+            if ((real_max_memuse or not dry_run)
+                and real_max_memuse < maxsize * memuse):
+                if verbose:
+                    sys.stderr.write("Skipping %s because of memory "
+                                     "constraint\n" % (f.__name__,))
+                return
 
             return f(self, maxsize)
         wrapper.size = size
@@ -1093,6 +1190,16 @@
             suite.addTest(unittest.makeSuite(cls))
     _run_suite(suite)
 
+#=======================================================================
+# Check for the presence of docstrings.
+
+HAVE_DOCSTRINGS = (check_impl_detail(cpython=False) or
+                   sys.platform == 'win32' or
+                   sysconfig.get_config_var('WITH_DOC_STRINGS'))
+
+requires_docstrings = unittest.skipUnless(HAVE_DOCSTRINGS,
+                                          "test requires docstrings")
+
 
 #=======================================================================
 # doctest driver.
@@ -1192,6 +1299,33 @@
             except:
                 break
 
+ at contextlib.contextmanager
+def swap_attr(obj, attr, new_val):
+    """Temporary swap out an attribute with a new object.
+
+    Usage:
+        with swap_attr(obj, "attr", 5):
+            ...
+
+        This will set obj.attr to 5 for the duration of the with: block,
+        restoring the old value at the end of the block. If `attr` doesn't
+        exist on `obj`, it will be created and then deleted at the end of the
+        block.
+    """
+    if hasattr(obj, attr):
+        real_val = getattr(obj, attr)
+        setattr(obj, attr, new_val)
+        try:
+            yield
+        finally:
+            setattr(obj, attr, real_val)
+    else:
+        setattr(obj, attr, new_val)
+        try:
+            yield
+        finally:
+            delattr(obj, attr)
+
 def py3k_bytes(b):
     """Emulate the py3k bytes() constructor.
 
diff --git a/lib-python/2.7/test/test_sys.py b/lib-python/2.7/test/test_sys.py
--- a/lib-python/2.7/test/test_sys.py
+++ b/lib-python/2.7/test/test_sys.py
@@ -490,22 +490,8 @@
 
 class SizeofTest(unittest.TestCase):
 
-    TPFLAGS_HAVE_GC = 1<<14
-    TPFLAGS_HEAPTYPE = 1L<<9
-
     def setUp(self):
-        self.c = len(struct.pack('c', ' '))
-        self.H = len(struct.pack('H', 0))
-        self.i = len(struct.pack('i', 0))
-        self.l = len(struct.pack('l', 0))
-        self.P = len(struct.pack('P', 0))
-        # due to missing size_t information from struct, it is assumed that
-        # sizeof(Py_ssize_t) = sizeof(void*)
-        self.header = 'PP'
-        self.vheader = self.header + 'P'
-        if hasattr(sys, "gettotalrefcount"):
-            self.header += '2P'
-            self.vheader += '2P'
+        self.P = struct.calcsize('P')
         self.longdigit = sys.long_info.sizeof_digit
         import _testcapi
         self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD
@@ -515,128 +501,109 @@
         self.file.close()
         test.test_support.unlink(test.test_support.TESTFN)
 
-    def check_sizeof(self, o, size):
-        result = sys.getsizeof(o)
-        if ((type(o) == type) and (o.__flags__ & self.TPFLAGS_HEAPTYPE) or\
-           ((type(o) != type) and (type(o).__flags__ & self.TPFLAGS_HAVE_GC))):
-            size += self.gc_headsize
-        msg = 'wrong size for %s: got %d, expected %d' \
-                % (type(o), result, size)
-        self.assertEqual(result, size, msg)
-
-    def calcsize(self, fmt):
-        """Wrapper around struct.calcsize which enforces the alignment of the
-        end of a structure to the alignment requirement of pointer.
-
-        Note: This wrapper should only be used if a pointer member is included
-        and no member with a size larger than a pointer exists.
-        """
-        return struct.calcsize(fmt + '0P')
+    check_sizeof = test.test_support.check_sizeof
 
     def test_gc_head_size(self):
         # Check that the gc header size is added to objects tracked by the gc.
-        h = self.header
-        size = self.calcsize
+        size = test.test_support.calcobjsize
         gc_header_size = self.gc_headsize
         # bool objects are not gc tracked
-        self.assertEqual(sys.getsizeof(True), size(h + 'l'))
+        self.assertEqual(sys.getsizeof(True), size('l'))
         # but lists are
-        self.assertEqual(sys.getsizeof([]), size(h + 'P PP') + gc_header_size)
+        self.assertEqual(sys.getsizeof([]), size('P PP') + gc_header_size)
 
     def test_default(self):
-        h = self.header
-        size = self.calcsize
-        self.assertEqual(sys.getsizeof(True, -1), size(h + 'l'))
+        size = test.test_support.calcobjsize
+        self.assertEqual(sys.getsizeof(True, -1), size('l'))
 
     def test_objecttypes(self):
         # check all types defined in Objects/
-        h = self.header
-        vh = self.vheader
-        size = self.calcsize
+        size = test.test_support.calcobjsize
+        vsize = test.test_support.calcvobjsize
         check = self.check_sizeof
         # bool
-        check(True, size(h + 'l'))
+        check(True, size('l'))
         # buffer
         with test.test_support.check_py3k_warnings():
-            check(buffer(''), size(h + '2P2Pil'))
+            check(buffer(''), size('2P2Pil'))
         # builtin_function_or_method
-        check(len, size(h + '3P'))
+        check(len, size('3P'))
         # bytearray
         samples = ['', 'u'*100000]
         for sample in samples:
             x = bytearray(sample)
-            check(x, size(vh + 'iPP') + x.__alloc__() * self.c)
+            check(x, vsize('iPP') + x.__alloc__())
         # bytearray_iterator
-        check(iter(bytearray()), size(h + 'PP'))
+        check(iter(bytearray()), size('PP'))
         # cell
         def get_cell():
             x = 42
             def inner():
                 return x
             return inner
-        check(get_cell().func_closure[0], size(h + 'P'))
+        check(get_cell().func_closure[0], size('P'))
         # classobj (old-style class)
         class class_oldstyle():
             def method():
                 pass
-        check(class_oldstyle, size(h + '7P'))
+        check(class_oldstyle, size('7P'))
         # instance (old-style class)
-        check(class_oldstyle(), size(h + '3P'))
+        check(class_oldstyle(), size('3P'))
         # instancemethod (old-style class)
-        check(class_oldstyle().method, size(h + '4P'))
+        check(class_oldstyle().method, size('4P'))
         # complex
-        check(complex(0,1), size(h + '2d'))
+        check(complex(0,1), size('2d'))
         # code
-        check(get_cell().func_code, size(h + '4i8Pi3P'))
+        check(get_cell().func_code, size('4i8Pi3P'))
         # BaseException
-        check(BaseException(), size(h + '3P'))
+        check(BaseException(), size('3P'))
         # UnicodeEncodeError
-        check(UnicodeEncodeError("", u"", 0, 0, ""), size(h + '5P2PP'))
+        check(UnicodeEncodeError("", u"", 0, 0, ""), size('5P2PP'))
         # UnicodeDecodeError
-        check(UnicodeDecodeError("", "", 0, 0, ""), size(h + '5P2PP'))
+        check(UnicodeDecodeError("", "", 0, 0, ""), size('5P2PP'))
         # UnicodeTranslateError
-        check(UnicodeTranslateError(u"", 0, 1, ""), size(h + '5P2PP'))
+        check(UnicodeTranslateError(u"", 0, 1, ""), size('5P2PP'))
         # method_descriptor (descriptor object)
-        check(str.lower, size(h + '2PP'))
+        check(str.lower, size('2PP'))
         # classmethod_descriptor (descriptor object)
         # XXX
         # member_descriptor (descriptor object)
         import datetime
-        check(datetime.timedelta.days, size(h + '2PP'))
+        check(datetime.timedelta.days, size('2PP'))
         # getset_descriptor (descriptor object)
         import __builtin__
-        check(__builtin__.file.closed, size(h + '2PP'))
+        check(__builtin__.file.closed, size('2PP'))
         # wrapper_descriptor (descriptor object)
-        check(int.__add__, size(h + '2P2P'))
+        check(int.__add__, size('2P2P'))
         # dictproxy
         class C(object): pass
-        check(C.__dict__, size(h + 'P'))
+        check(C.__dict__, size('P'))
         # method-wrapper (descriptor object)
-        check({}.__iter__, size(h + '2P'))
+        check({}.__iter__, size('2P'))
         # dict
-        check({}, size(h + '3P2P' + 8*'P2P'))
+        check({}, size('3P2P' + 8*'P2P'))
         x = {1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8}
-        check(x, size(h + '3P2P' + 8*'P2P') + 16*size('P2P'))
+        check(x, size('3P2P' + 8*'P2P') + 16*struct.calcsize('P2P'))
         # dictionary-keyiterator
-        check({}.iterkeys(), size(h + 'P2PPP'))
+        check({}.iterkeys(), size('P2PPP'))
         # dictionary-valueiterator
-        check({}.itervalues(), size(h + 'P2PPP'))
+        check({}.itervalues(), size('P2PPP'))
         # dictionary-itemiterator
-        check({}.iteritems(), size(h + 'P2PPP'))
+        check({}.iteritems(), size('P2PPP'))
         # ellipses
-        check(Ellipsis, size(h + ''))
+        check(Ellipsis, size(''))
         # EncodingMap
         import codecs, encodings.iso8859_3
         x = codecs.charmap_build(encodings.iso8859_3.decoding_table)
-        check(x, size(h + '32B2iB'))
+        check(x, size('32B2iB'))
         # enumerate
-        check(enumerate([]), size(h + 'l3P'))
+        check(enumerate([]), size('l3P'))
         # file
-        check(self.file, size(h + '4P2i4P3i3P3i'))
+        check(self.file, size('4P2i4P3i3P3i'))
         # float
-        check(float(0), size(h + 'd'))
+        check(float(0), size('d'))
         # sys.floatinfo
-        check(sys.float_info, size(vh) + self.P * len(sys.float_info))
+        check(sys.float_info, vsize('') + self.P * len(sys.float_info))
         # frame
         import inspect
         CO_MAXBLOCKS = 20
@@ -645,10 +612,10 @@
         nfrees = len(x.f_code.co_freevars)
         extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
                  ncells + nfrees - 1
-        check(x, size(vh + '12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
+        check(x, vsize('12P3i' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
         # function
         def func(): pass
-        check(func, size(h + '9P'))
+        check(func, size('9P'))
         class c():
             @staticmethod
             def foo():
@@ -657,65 +624,65 @@
             def bar(cls):
                 pass
             # staticmethod
-            check(foo, size(h + 'P'))
+            check(foo, size('P'))
             # classmethod
-            check(bar, size(h + 'P'))
+            check(bar, size('P'))
         # generator
         def get_gen(): yield 1
-        check(get_gen(), size(h + 'Pi2P'))
+        check(get_gen(), size('Pi2P'))
         # integer
-        check(1, size(h + 'l'))
-        check(100, size(h + 'l'))
+        check(1, size('l'))
+        check(100, size('l'))
         # iterator
-        check(iter('abc'), size(h + 'lP'))
+        check(iter('abc'), size('lP'))
         # callable-iterator
         import re
-        check(re.finditer('',''), size(h + '2P'))
+        check(re.finditer('',''), size('2P'))
         # list
         samples = [[], [1,2,3], ['1', '2', '3']]
         for sample in samples:
-            check(sample, size(vh + 'PP') + len(sample)*self.P)
+            check(sample, vsize('PP') + len(sample)*self.P)
         # sortwrapper (list)
         # XXX
         # cmpwrapper (list)
         # XXX
         # listiterator (list)
-        check(iter([]), size(h + 'lP'))
+        check(iter([]), size('lP'))
         # listreverseiterator (list)
-        check(reversed([]), size(h + 'lP'))
+        check(reversed([]), size('lP'))
         # long
-        check(0L, size(vh))
-        check(1L, size(vh) + self.longdigit)
-        check(-1L, size(vh) + self.longdigit)
+        check(0L, vsize(''))
+        check(1L, vsize('') + self.longdigit)
+        check(-1L, vsize('') + self.longdigit)
         PyLong_BASE = 2**sys.long_info.bits_per_digit
-        check(long(PyLong_BASE), size(vh) + 2*self.longdigit)
-        check(long(PyLong_BASE**2-1), size(vh) + 2*self.longdigit)
-        check(long(PyLong_BASE**2), size(vh) + 3*self.longdigit)
+        check(long(PyLong_BASE), vsize('') + 2*self.longdigit)
+        check(long(PyLong_BASE**2-1), vsize('') + 2*self.longdigit)
+        check(long(PyLong_BASE**2), vsize('') + 3*self.longdigit)
         # module
-        check(unittest, size(h + 'P'))
+        check(unittest, size('P'))
         # None
-        check(None, size(h + ''))
+        check(None, size(''))
         # object
-        check(object(), size(h + ''))
+        check(object(), size(''))
         # property (descriptor object)
         class C(object):
             def getx(self): return self.__x
             def setx(self, value): self.__x = value
             def delx(self): del self.__x
             x = property(getx, setx, delx, "")
-            check(x, size(h + '4Pi'))
+            check(x, size('4Pi'))
         # PyCObject
         # PyCapsule
         # XXX
         # rangeiterator
-        check(iter(xrange(1)), size(h + '4l'))
+        check(iter(xrange(1)), size('4l'))
         # reverse
-        check(reversed(''), size(h + 'PP'))
+        check(reversed(''), size('PP'))
         # set
         # frozenset
         PySet_MINSIZE = 8
         samples = [[], range(10), range(50)]
-        s = size(h + '3P2P' + PySet_MINSIZE*'lP' + 'lP')
+        s = size('3P2P' + PySet_MINSIZE*'lP' + 'lP')
         for sample in samples:
             minused = len(sample)
             if minused == 0: tmp = 1
@@ -732,23 +699,24 @@
                 check(set(sample), s + newsize*struct.calcsize('lP'))
                 check(frozenset(sample), s + newsize*struct.calcsize('lP'))
         # setiterator
-        check(iter(set()), size(h + 'P3P'))
+        check(iter(set()), size('P3P'))
         # slice
-        check(slice(1), size(h + '3P'))
+        check(slice(1), size('3P'))
         # str
-        check('', struct.calcsize(vh + 'li') + 1)
-        check('abc', struct.calcsize(vh + 'li') + 1 + 3*self.c)
+        vh = test.test_support._vheader
+        check('', struct.calcsize(vh + 'lic'))
+        check('abc', struct.calcsize(vh + 'lic') + 3)
         # super
-        check(super(int), size(h + '3P'))
+        check(super(int), size('3P'))
         # tuple
-        check((), size(vh))
-        check((1,2,3), size(vh) + 3*self.P)
+        check((), vsize(''))
+        check((1,2,3), vsize('') + 3*self.P)
         # tupleiterator
-        check(iter(()), size(h + 'lP'))
+        check(iter(()), size('lP'))
         # type
         # (PyTypeObject + PyNumberMethods +  PyMappingMethods +
         #  PySequenceMethods + PyBufferProcs)
-        s = size(vh + 'P2P15Pl4PP9PP11PI') + size('41P 10P 3P 6P')
+        s = vsize('P2P15Pl4PP9PP11PI') + struct.calcsize('41P 10P 3P 6P')
         class newstyleclass(object):
             pass
         check(newstyleclass, s)
@@ -763,41 +731,40 @@
         # we need to test for both sizes, because we don't know if the string
         # has been cached
         for s in samples:
-            check(s, size(h + 'PPlP') + usize * (len(s) + 1))
+            check(s, size('PPlP') + usize * (len(s) + 1))
         # weakref
         import weakref
-        check(weakref.ref(int), size(h + '2Pl2P'))
+        check(weakref.ref(int), size('2Pl2P'))
         # weakproxy
         # XXX
         # weakcallableproxy
-        check(weakref.proxy(int), size(h + '2Pl2P'))
+        check(weakref.proxy(int), size('2Pl2P'))
         # xrange
-        check(xrange(1), size(h + '3l'))
-        check(xrange(66000), size(h + '3l'))
+        check(xrange(1), size('3l'))
+        check(xrange(66000), size('3l'))
 
     def test_pythontypes(self):
         # check all types defined in Python/
-        h = self.header
-        vh = self.vheader
-        size = self.calcsize
+        size = test.test_support.calcobjsize
+        vsize = test.test_support.calcvobjsize
         check = self.check_sizeof
         # _ast.AST
         import _ast
-        check(_ast.AST(), size(h + ''))
+        check(_ast.AST(), size(''))
         # imp.NullImporter
         import imp
-        check(imp.NullImporter(self.file.name), size(h + ''))
+        check(imp.NullImporter(self.file.name), size(''))
         try:
             raise TypeError
         except TypeError:
             tb = sys.exc_info()[2]
             # traceback
             if tb != None:
-                check(tb, size(h + '2P2i'))
+                check(tb, size('2P2i'))
         # symtable entry
         # XXX
         # sys.flags
-        check(sys.flags, size(vh) + self.P * len(sys.flags))
+        check(sys.flags, vsize('') + self.P * len(sys.flags))
 
 
 def test_main():
diff --git a/lib-python/2.7/test/test_sys_settrace.py b/lib-python/2.7/test/test_sys_settrace.py
--- a/lib-python/2.7/test/test_sys_settrace.py
+++ b/lib-python/2.7/test/test_sys_settrace.py
@@ -417,7 +417,7 @@
                 except ValueError:
                     pass
                 else:
-                    self.fail("exception not thrown!")
+                    self.fail("exception not raised!")
         except RuntimeError:
             self.fail("recursion counter not reset")
 
@@ -670,6 +670,14 @@
 no_jump_to_non_integers.jump = (2, "Spam")
 no_jump_to_non_integers.output = [True]
 
+def jump_across_with(output):
+    with open(test_support.TESTFN, "wb") as fp:
+        pass
+    with open(test_support.TESTFN, "wb") as fp:
+        pass
+jump_across_with.jump = (1, 3)
+jump_across_with.output = []
+
 # This verifies that you can't set f_lineno via _getframe or similar
 # trickery.
 def no_jump_without_trace_function():
@@ -739,6 +747,9 @@
         self.run_test(no_jump_to_non_integers)
     def test_19_no_jump_without_trace_function(self):
         no_jump_without_trace_function()
+    def test_jump_across_with(self):
+        self.addCleanup(test_support.unlink, test_support.TESTFN)
+        self.run_test(jump_across_with)
 
     def test_20_large_function(self):
         d = {}
diff --git a/lib-python/2.7/test/test_sysconfig.py b/lib-python/2.7/test/test_sysconfig.py
--- a/lib-python/2.7/test/test_sysconfig.py
+++ b/lib-python/2.7/test/test_sysconfig.py
@@ -14,6 +14,7 @@
                        get_path, get_path_names, _INSTALL_SCHEMES,
                        _get_default_scheme, _expand_vars,
                        get_scheme_names, get_config_var)
+import _osx_support
 
 class TestSysConfig(unittest.TestCase):
 
@@ -137,6 +138,7 @@
                    ('Darwin Kernel Version 8.11.1: '
                     'Wed Oct 10 18:23:28 PDT 2007; '
                     'root:xnu-792.25.20~1/RELEASE_I386'), 'PowerPC'))
+        _osx_support._remove_original_values(get_config_vars())
         get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
 
         get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g '
@@ -156,6 +158,7 @@
                    ('Darwin Kernel Version 8.11.1: '
                     'Wed Oct 10 18:23:28 PDT 2007; '
                     'root:xnu-792.25.20~1/RELEASE_I386'), 'i386'))
+        _osx_support._remove_original_values(get_config_vars())
         get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
 
         get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g '
@@ -171,6 +174,7 @@
             sys.maxint = maxint
 
         # macbook with fat binaries (fat, universal or fat64)
+        _osx_support._remove_original_values(get_config_vars())
         get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4'
         get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
@@ -179,6 +183,7 @@
 
         self.assertEqual(get_platform(), 'macosx-10.4-fat')
 
+        _osx_support._remove_original_values(get_config_vars())
         get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
@@ -186,18 +191,21 @@
 
         self.assertEqual(get_platform(), 'macosx-10.4-intel')
 
+        _osx_support._remove_original_values(get_config_vars())
         get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
                                        '-dynamic -DNDEBUG -g -O3')
         self.assertEqual(get_platform(), 'macosx-10.4-fat3')
 
+        _osx_support._remove_original_values(get_config_vars())
         get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
                                        '-dynamic -DNDEBUG -g -O3')
         self.assertEqual(get_platform(), 'macosx-10.4-universal')
 
+        _osx_support._remove_original_values(get_config_vars())
         get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot '
                                        '/Developer/SDKs/MacOSX10.4u.sdk  '
                                        '-fno-strict-aliasing -fno-common '
@@ -206,6 +214,7 @@
         self.assertEqual(get_platform(), 'macosx-10.4-fat64')
 
         for arch in ('ppc', 'i386', 'x86_64', 'ppc64'):
+            _osx_support._remove_original_values(get_config_vars())
             get_config_vars()['CFLAGS'] = ('-arch %s -isysroot '
                                            '/Developer/SDKs/MacOSX10.4u.sdk  '
                                            '-fno-strict-aliasing -fno-common '
diff --git a/lib-python/2.7/test/test_tarfile.py b/lib-python/2.7/test/test_tarfile.py
--- a/lib-python/2.7/test/test_tarfile.py
+++ b/lib-python/2.7/test/test_tarfile.py
@@ -154,6 +154,9 @@
     def test_fileobj_symlink2(self):
         self._test_fileobj_link("./ustar/linktest2/symtype", "ustar/linktest1/regtype")
 
+    def test_issue14160(self):
+        self._test_fileobj_link("symtype2", "ustar/regtype")
+
 
 class CommonReadTest(ReadTest):
 
@@ -294,26 +297,21 @@
 
     def test_extract_hardlink(self):
         # Test hardlink extraction (e.g. bug #857297).
-        tar = tarfile.open(tarname, errorlevel=1, encoding="iso8859-1")
+        with tarfile.open(tarname, errorlevel=1, encoding="iso8859-1") as tar:
+            tar.extract("ustar/regtype", TEMPDIR)
+            self.addCleanup(os.remove, os.path.join(TEMPDIR, "ustar/regtype"))
 
-        tar.extract("ustar/regtype", TEMPDIR)
-        try:
             tar.extract("ustar/lnktype", TEMPDIR)
-        except EnvironmentError, e:
-            if e.errno == errno.ENOENT:
-                self.fail("hardlink not extracted properly")
+            self.addCleanup(os.remove, os.path.join(TEMPDIR, "ustar/lnktype"))
+            with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f:
+                data = f.read()
+            self.assertEqual(md5sum(data), md5_regtype)
 
-        data = open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb").read()
-        self.assertEqual(md5sum(data), md5_regtype)
-
-        try:
             tar.extract("ustar/symtype", TEMPDIR)
-        except EnvironmentError, e:
-            if e.errno == errno.ENOENT:
-                self.fail("symlink not extracted properly")
-
-        data = open(os.path.join(TEMPDIR, "ustar/symtype"), "rb").read()
-        self.assertEqual(md5sum(data), md5_regtype)
+            self.addCleanup(os.remove, os.path.join(TEMPDIR, "ustar/symtype"))
+            with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f:
+                data = f.read()
+            self.assertEqual(md5sum(data), md5_regtype)
 
     def test_extractall(self):
         # Test if extractall() correctly restores directory permissions
@@ -855,7 +853,7 @@
 
             tar = tarfile.open(tmpname, "r")
             for t in tar:
-                self.assert_(t.name == "." or t.name.startswith("./"))
+                self.assertTrue(t.name == "." or t.name.startswith("./"))
             tar.close()
         finally:
             os.chdir(cwd)
diff --git a/lib-python/2.7/test/test_tcl.py b/lib-python/2.7/test/test_tcl.py
--- a/lib-python/2.7/test/test_tcl.py
+++ b/lib-python/2.7/test/test_tcl.py
@@ -1,6 +1,7 @@
 #!/usr/bin/env python
 
 import unittest
+import sys
 import os
 from test import test_support
 
@@ -151,6 +152,27 @@
         # exit code must be zero
         self.assertEqual(f.close(), None)
 
+    def test_passing_values(self):
+        def passValue(value):
+            return self.interp.call('set', '_', value)
+        self.assertEqual(passValue(True), True)
+        self.assertEqual(passValue(False), False)
+        self.assertEqual(passValue('string'), 'string')
+        self.assertEqual(passValue('string\u20ac'), 'string\u20ac')
+        self.assertEqual(passValue(u'string'), u'string')
+        self.assertEqual(passValue(u'string\u20ac'), u'string\u20ac')
+        for i in (0, 1, -1, int(2**31-1), int(-2**31)):
+            self.assertEqual(passValue(i), i)
+        for f in (0.0, 1.0, -1.0, 1//3, 1/3.0,
+                  sys.float_info.min, sys.float_info.max,
+                  -sys.float_info.min, -sys.float_info.max):
+            self.assertEqual(passValue(f), f)
+        for f in float('nan'), float('inf'), -float('inf'):
+            if f != f: # NaN
+                self.assertNotEqual(passValue(f), f)
+            else:
+                self.assertEqual(passValue(f), f)
+        self.assertEqual(passValue((1, '2', (3.4,))), (1, '2', (3.4,)))
 
 
 def test_main():
diff --git a/lib-python/2.7/test/test_telnetlib.py b/lib-python/2.7/test/test_telnetlib.py
--- a/lib-python/2.7/test/test_telnetlib.py
+++ b/lib-python/2.7/test/test_telnetlib.py
@@ -3,6 +3,7 @@
 import time
 import Queue
 
+import unittest
 from unittest import TestCase
 from test import test_support
 threading = test_support.import_module('threading')
@@ -135,6 +136,28 @@
         self.assertEqual(data, want[0])
         self.assertEqual(telnet.read_all(), 'not seen')
 
+    def test_read_until_with_poll(self):
+        """Use select.poll() to implement telnet.read_until()."""
+        want = ['x' * 10, 'match', 'y' * 10, EOF_sigil]
+        self.dataq.put(want)
+        telnet = telnetlib.Telnet(HOST, self.port)
+        if not telnet._has_poll:
+            raise unittest.SkipTest('select.poll() is required')
+        telnet._has_poll = True
+        self.dataq.join()
+        data = telnet.read_until('match')
+        self.assertEqual(data, ''.join(want[:-2]))
+
+    def test_read_until_with_select(self):
+        """Use select.select() to implement telnet.read_until()."""
+        want = ['x' * 10, 'match', 'y' * 10, EOF_sigil]
+        self.dataq.put(want)
+        telnet = telnetlib.Telnet(HOST, self.port)
+        telnet._has_poll = False
+        self.dataq.join()
+        data = telnet.read_until('match')
+        self.assertEqual(data, ''.join(want[:-2]))
+
     def test_read_all_A(self):
         """
         read_all()
@@ -357,8 +380,75 @@
         self.assertEqual('', telnet.read_sb_data())
         nego.sb_getter = None # break the nego => telnet cycle
 
+
+class ExpectTests(TestCase):
+    def setUp(self):
+        self.evt = threading.Event()
+        self.dataq = Queue.Queue()
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sock.settimeout(10)
+        self.port = test_support.bind_port(self.sock)
+        self.thread = threading.Thread(target=server, args=(self.evt,self.sock,
+                                                            self.dataq))
+        self.thread.start()
+        self.evt.wait()
+
+    def tearDown(self):
+        self.thread.join()
+
+    # use a similar approach to testing timeouts as test_timeout.py
+    # these will never pass 100% but make the fuzz big enough that it is rare
+    block_long = 0.6
+    block_short = 0.3
+    def test_expect_A(self):
+        """
+        expect(expected, [timeout])
+          Read until the expected string has been seen, or a timeout is
+          hit (default is no timeout); may block.
+        """
+        want = ['x' * 10, 'match', 'y' * 10, EOF_sigil]
+        self.dataq.put(want)
+        telnet = telnetlib.Telnet(HOST, self.port)
+        self.dataq.join()
+        (_,_,data) = telnet.expect(['match'])
+        self.assertEqual(data, ''.join(want[:-2]))
+
+    def test_expect_B(self):
+        # test the timeout - it does NOT raise socket.timeout
+        want = ['hello', self.block_long, 'not seen', EOF_sigil]
+        self.dataq.put(want)
+        telnet = telnetlib.Telnet(HOST, self.port)
+        self.dataq.join()
+        (_,_,data) = telnet.expect(['not seen'], self.block_short)
+        self.assertEqual(data, want[0])
+        self.assertEqual(telnet.read_all(), 'not seen')
+
+    def test_expect_with_poll(self):
+        """Use select.poll() to implement telnet.expect()."""
+        want = ['x' * 10, 'match', 'y' * 10, EOF_sigil]
+        self.dataq.put(want)
+        telnet = telnetlib.Telnet(HOST, self.port)
+        if not telnet._has_poll:
+            raise unittest.SkipTest('select.poll() is required')
+        telnet._has_poll = True
+        self.dataq.join()
+        (_,_,data) = telnet.expect(['match'])
+        self.assertEqual(data, ''.join(want[:-2]))
+
+    def test_expect_with_select(self):
+        """Use select.select() to implement telnet.expect()."""
+        want = ['x' * 10, 'match', 'y' * 10, EOF_sigil]
+        self.dataq.put(want)
+        telnet = telnetlib.Telnet(HOST, self.port)
+        telnet._has_poll = False
+        self.dataq.join()
+        (_,_,data) = telnet.expect(['match'])
+        self.assertEqual(data, ''.join(want[:-2]))
+
+
 def test_main(verbose=None):
-    test_support.run_unittest(GeneralTests, ReadTests, OptionTests)
+    test_support.run_unittest(GeneralTests, ReadTests, OptionTests,
+                              ExpectTests)
 
 if __name__ == '__main__':
     test_main()
diff --git a/lib-python/2.7/test/test_tempfile.py b/lib-python/2.7/test/test_tempfile.py
--- a/lib-python/2.7/test/test_tempfile.py
+++ b/lib-python/2.7/test/test_tempfile.py
@@ -1,13 +1,16 @@
 # tempfile.py unit tests.
 import tempfile
+import errno
+import io
 import os
 import signal
+import shutil
 import sys
 import re
 import warnings
 
 import unittest
-from test import test_support
+from test import test_support as support
 
 warnings.filterwarnings("ignore",
                         category=RuntimeWarning,
@@ -177,7 +180,7 @@
         # _candidate_tempdir_list contains the expected directories
 
         # Make sure the interesting environment variables are all set.
-        with test_support.EnvironmentVarGuard() as env:
+        with support.EnvironmentVarGuard() as env:
             for envname in 'TMPDIR', 'TEMP', 'TMP':
                 dirname = os.getenv(envname)
                 if not dirname:
@@ -202,8 +205,51 @@
 
 test_classes.append(test__candidate_tempdir_list)
 
+# We test _get_default_tempdir some more by testing gettempdir.
 
-# We test _get_default_tempdir by testing gettempdir.
+class TestGetDefaultTempdir(TC):
+    """Test _get_default_tempdir()."""
+
+    def test_no_files_left_behind(self):
+        # use a private empty directory
+        our_temp_directory = tempfile.mkdtemp()
+        try:
+            # force _get_default_tempdir() to consider our empty directory
+            def our_candidate_list():
+                return [our_temp_directory]
+
+            with support.swap_attr(tempfile, "_candidate_tempdir_list",
+                                   our_candidate_list):
+                # verify our directory is empty after _get_default_tempdir()
+                tempfile._get_default_tempdir()
+                self.assertEqual(os.listdir(our_temp_directory), [])
+
+                def raise_OSError(*args, **kwargs):
+                    raise OSError(-1)
+
+                with support.swap_attr(io, "open", raise_OSError):
+                    # test again with failing io.open()
+                    with self.assertRaises(IOError) as cm:
+                        tempfile._get_default_tempdir()
+                    self.assertEqual(cm.exception.errno, errno.ENOENT)
+                    self.assertEqual(os.listdir(our_temp_directory), [])
+
+                open = io.open
+                def bad_writer(*args, **kwargs):
+                    fp = open(*args, **kwargs)
+                    fp.write = raise_OSError
+                    return fp
+
+                with support.swap_attr(io, "open", bad_writer):
+                    # test again with failing write()
+                    with self.assertRaises(IOError) as cm:
+                        tempfile._get_default_tempdir()
+                    self.assertEqual(cm.exception.errno, errno.ENOENT)
+                    self.assertEqual(os.listdir(our_temp_directory), [])
+        finally:
+            shutil.rmtree(our_temp_directory)
+
+test_classes.append(TestGetDefaultTempdir)
 
 
 class test__get_candidate_names(TC):
@@ -299,7 +345,7 @@
         if not has_spawnl:
             return            # ugh, can't use SkipTest.
 
-        if test_support.verbose:
+        if support.verbose:
             v="v"
         else:
             v="q"
@@ -738,6 +784,17 @@
         f.write(b'x')
         self.assertTrue(f._rolled)
 
+    def test_xreadlines(self):
+        f = self.do_create(max_size=20)
+        f.write(b'abc\n' * 5)
+        f.seek(0)
+        self.assertFalse(f._rolled)
+        self.assertEqual(list(f.xreadlines()), [b'abc\n'] * 5)
+        f.write(b'x\ny')
+        self.assertTrue(f._rolled)
+        f.seek(0)
+        self.assertEqual(list(f.xreadlines()), [b'abc\n'] * 5 + [b'x\n', b'y'])
+
     def test_sparse(self):
         # A SpooledTemporaryFile that is written late in the file will extend
         # when that occurs
@@ -793,6 +850,26 @@
         seek(0, 0)
         self.assertTrue(read(70) == 'a'*35 + 'b'*35)
 
+    def test_properties(self):
+        f = tempfile.SpooledTemporaryFile(max_size=10)
+        f.write(b'x' * 10)
+        self.assertFalse(f._rolled)
+        self.assertEqual(f.mode, 'w+b')
+        self.assertIsNone(f.name)
+        with self.assertRaises(AttributeError):
+            f.newlines
+        with self.assertRaises(AttributeError):
+            f.encoding
+
+        f.write(b'x')
+        self.assertTrue(f._rolled)
+        self.assertEqual(f.mode, 'w+b')
+        self.assertIsNotNone(f.name)
+        with self.assertRaises(AttributeError):
+            f.newlines
+        with self.assertRaises(AttributeError):
+            f.encoding
+
     def test_context_manager_before_rollover(self):
         # A SpooledTemporaryFile can be used as a context manager
         with tempfile.SpooledTemporaryFile(max_size=1) as f:
@@ -882,7 +959,7 @@
     test_classes.append(test_TemporaryFile)
 
 def test_main():
-    test_support.run_unittest(*test_classes)
+    support.run_unittest(*test_classes)
 
 if __name__ == "__main__":
     test_main()
diff --git a/lib-python/2.7/test/test_textwrap.py b/lib-python/2.7/test/test_textwrap.py
--- a/lib-python/2.7/test/test_textwrap.py
+++ b/lib-python/2.7/test/test_textwrap.py
@@ -66,6 +66,15 @@
                          "I'm glad to hear it!"])
         self.check_wrap(text, 80, [text])
 
+    def test_empty_string(self):
+        # Check that wrapping the empty string returns an empty list.
+        self.check_wrap("", 6, [])
+        self.check_wrap("", 6, [], drop_whitespace=False)
+
+    def test_empty_string_with_initial_indent(self):
+        # Check that the empty string is not indented.
+        self.check_wrap("", 6, [], initial_indent="++")
+        self.check_wrap("", 6, [], initial_indent="++", drop_whitespace=False)
 
     def test_whitespace(self):
         # Whitespace munging and end-of-sentence detection
@@ -323,7 +332,32 @@
                          ["blah", " ", "(ding", " ", "dong),",
                           " ", "wubba"])
 
-    def test_initial_whitespace(self):
+    def test_drop_whitespace_false(self):
+        # Check that drop_whitespace=False preserves whitespace.
+        # SF patch #1581073
+        text = " This is a    sentence with     much whitespace."
+        self.check_wrap(text, 10,
+                        [" This is a", "    ", "sentence ",
+                         "with     ", "much white", "space."],
+                        drop_whitespace=False)
+
+    def test_drop_whitespace_false_whitespace_only(self):
+        # Check that drop_whitespace=False preserves a whitespace-only string.
+        self.check_wrap("   ", 6, ["   "], drop_whitespace=False)
+
+    def test_drop_whitespace_false_whitespace_only_with_indent(self):
+        # Check that a whitespace-only string gets indented (when
+        # drop_whitespace is False).
+        self.check_wrap("   ", 6, ["     "], drop_whitespace=False,
+                        initial_indent="  ")
+
+    def test_drop_whitespace_whitespace_only(self):
+        # Check drop_whitespace on a whitespace-only string.
+        self.check_wrap("  ", 6, [])
+
+    def test_drop_whitespace_leading_whitespace(self):
+        # Check that drop_whitespace does not drop leading whitespace (if
+        # followed by non-whitespace).
         # SF bug #622849 reported inconsistent handling of leading
         # whitespace; let's test that a bit, shall we?
         text = " This is a sentence with leading whitespace."
@@ -332,13 +366,27 @@
         self.check_wrap(text, 30,
                         [" This is a sentence with", "leading whitespace."])
 
-    def test_no_drop_whitespace(self):
-        # SF patch #1581073
-        text = " This is a    sentence with     much whitespace."
-        self.check_wrap(text, 10,
-                        [" This is a", "    ", "sentence ",
-                         "with     ", "much white", "space."],
+    def test_drop_whitespace_whitespace_line(self):
+        # Check that drop_whitespace skips the whole line if a non-leading
+        # line consists only of whitespace.
+        text = "abcd    efgh"
+        # Include the result for drop_whitespace=False for comparison.
+        self.check_wrap(text, 6, ["abcd", "    ", "efgh"],
                         drop_whitespace=False)
+        self.check_wrap(text, 6, ["abcd", "efgh"])
+
+    def test_drop_whitespace_whitespace_only_with_indent(self):
+        # Check that initial_indent is not applied to a whitespace-only
+        # string.  This checks a special case of the fact that dropping
+        # whitespace occurs before indenting.
+        self.check_wrap("  ", 6, [], initial_indent="++")
+
+    def test_drop_whitespace_whitespace_indent(self):
+        # Check that drop_whitespace does not drop whitespace indents.
+        # This checks a special case of the fact that dropping whitespace
+        # occurs before indenting.
+        self.check_wrap("abcd efgh", 6, ["  abcd", "  efgh"],
+                        initial_indent="  ", subsequent_indent="  ")
 
     if test_support.have_unicode:
         def test_unicode(self):
diff --git a/lib-python/2.7/test/test_thread.py b/lib-python/2.7/test/test_thread.py
--- a/lib-python/2.7/test/test_thread.py
+++ b/lib-python/2.7/test/test_thread.py
@@ -130,6 +130,29 @@
             time.sleep(0.01)
         self.assertEqual(thread._count(), orig)
 
+    def test_save_exception_state_on_error(self):
+        # See issue #14474
+        def task():
+            started.release()
+            raise SyntaxError
+        def mywrite(self, *args):
+            try:
+                raise ValueError
+            except ValueError:
+                pass
+            real_write(self, *args)
+        c = thread._count()
+        started = thread.allocate_lock()
+        with test_support.captured_output("stderr") as stderr:
+            real_write = stderr.write
+            stderr.write = mywrite
+            started.acquire()
+            thread.start_new_thread(task, ())
+            started.acquire()
+            while thread._count() > c:
+                time.sleep(0.01)
+        self.assertIn("Traceback", stderr.getvalue())
+
 
 class Barrier:
     def __init__(self, num_threads):
diff --git a/lib-python/2.7/test/test_threading.py b/lib-python/2.7/test/test_threading.py
--- a/lib-python/2.7/test/test_threading.py
+++ b/lib-python/2.7/test/test_threading.py
@@ -2,6 +2,8 @@
 
 import test.test_support
 from test.test_support import verbose
+from test.script_helper import assert_python_ok
+
 import random
 import re
 import sys
@@ -414,6 +416,33 @@
                          msg=('%d references still around' %
                               sys.getrefcount(weak_raising_cyclic_object())))
 
+    @unittest.skipUnless(hasattr(os, 'fork'), 'test needs fork()')
+    def test_dummy_thread_after_fork(self):
+        # Issue #14308: a dummy thread in the active list doesn't mess up
+        # the after-fork mechanism.
+        code = """if 1:
+            import thread, threading, os, time
+
+            def background_thread(evt):
+                # Creates and registers the _DummyThread instance
+                threading.current_thread()
+                evt.set()
+                time.sleep(10)
+
+            evt = threading.Event()
+            thread.start_new_thread(background_thread, (evt,))
+            evt.wait()
+            assert threading.active_count() == 2, threading.active_count()
+            if os.fork() == 0:
+                assert threading.active_count() == 1, threading.active_count()
+                os._exit(0)
+            else:
+                os.wait()
+        """
+        _, out, err = assert_python_ok("-c", code)
+        self.assertEqual(out, '')
+        self.assertEqual(err, '')
+
 
 class ThreadJoinOnShutdown(BaseTestCase):
 
diff --git a/lib-python/2.7/test/test_time.py b/lib-python/2.7/test/test_time.py
--- a/lib-python/2.7/test/test_time.py
+++ b/lib-python/2.7/test/test_time.py
@@ -106,7 +106,7 @@
 
     def test_strptime(self):
         # Should be able to go round-trip from strftime to strptime without
-        # throwing an exception.
+        # raising an exception.
         tt = time.gmtime(self.t)
         for directive in ('a', 'A', 'b', 'B', 'c', 'd', 'H', 'I',
                           'j', 'm', 'M', 'p', 'S',
diff --git a/lib-python/2.7/test/test_tokenize.py b/lib-python/2.7/test/test_tokenize.py
--- a/lib-python/2.7/test/test_tokenize.py
+++ b/lib-python/2.7/test/test_tokenize.py
@@ -278,6 +278,31 @@
     OP         '+'           (1, 32) (1, 33)
     STRING     'UR"ABC"'     (1, 34) (1, 41)
 
+    >>> dump_tokens("b'abc' + B'abc'")
+    STRING     "b'abc'"      (1, 0) (1, 6)
+    OP         '+'           (1, 7) (1, 8)
+    STRING     "B'abc'"      (1, 9) (1, 15)
+    >>> dump_tokens('b"abc" + B"abc"')
+    STRING     'b"abc"'      (1, 0) (1, 6)
+    OP         '+'           (1, 7) (1, 8)
+    STRING     'B"abc"'      (1, 9) (1, 15)
+    >>> dump_tokens("br'abc' + bR'abc' + Br'abc' + BR'abc'")
+    STRING     "br'abc'"     (1, 0) (1, 7)
+    OP         '+'           (1, 8) (1, 9)
+    STRING     "bR'abc'"     (1, 10) (1, 17)
+    OP         '+'           (1, 18) (1, 19)
+    STRING     "Br'abc'"     (1, 20) (1, 27)
+    OP         '+'           (1, 28) (1, 29)
+    STRING     "BR'abc'"     (1, 30) (1, 37)
+    >>> dump_tokens('br"abc" + bR"abc" + Br"abc" + BR"abc"')
+    STRING     'br"abc"'     (1, 0) (1, 7)
+    OP         '+'           (1, 8) (1, 9)
+    STRING     'bR"abc"'     (1, 10) (1, 17)
+    OP         '+'           (1, 18) (1, 19)
+    STRING     'Br"abc"'     (1, 20) (1, 27)
+    OP         '+'           (1, 28) (1, 29)
+    STRING     'BR"abc"'     (1, 30) (1, 37)
+
 Operators
 
     >>> dump_tokens("def d22(a, b, c=2, d=2, *k): pass")
@@ -525,6 +550,10 @@
     NAME       'pass'        (3, 9) (3, 13)
     DEDENT     ''            (4, 0) (4, 0)
     DEDENT     ''            (4, 0) (4, 0)
+
+Pathological whitespace (http://bugs.python.org/issue16152)
+    >>> dump_tokens("@          ")
+    OP         '@'           (1, 0) (1, 1)
 """
 
 
diff --git a/lib-python/2.7/test/test_tools.py b/lib-python/2.7/test/test_tools.py
--- a/lib-python/2.7/test/test_tools.py
+++ b/lib-python/2.7/test/test_tools.py
@@ -5,22 +5,28 @@
 """
 
 import os
+import sys
 import unittest
+import shutil
+import subprocess
 import sysconfig
+import tempfile
+import textwrap
 from test import test_support
-from test.script_helper import assert_python_ok
+from test.script_helper import assert_python_ok, temp_dir
 
 if not sysconfig.is_python_build():
     # XXX some installers do contain the tools, should we detect that
     # and run the tests in that case too?
     raise unittest.SkipTest('test irrelevant for an installed Python')
 
-srcdir = sysconfig.get_config_var('projectbase')
-basepath = os.path.join(os.getcwd(), srcdir, 'Tools')
+basepath = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
+                        'Tools')
+scriptsdir = os.path.join(basepath, 'scripts')
 
 
 class ReindentTests(unittest.TestCase):
-    script = os.path.join(basepath, 'scripts', 'reindent.py')
+    script = os.path.join(scriptsdir, 'reindent.py')
 
     def test_noargs(self):
         assert_python_ok(self.script)
@@ -31,8 +37,331 @@
         self.assertGreater(err, b'')
 
 
+class PindentTests(unittest.TestCase):
+    script = os.path.join(scriptsdir, 'pindent.py')
+
+    def assertFileEqual(self, fn1, fn2):
+        with open(fn1) as f1, open(fn2) as f2:
+            self.assertEqual(f1.readlines(), f2.readlines())
+
+    def pindent(self, source, *args):
+        proc = subprocess.Popen(
+                (sys.executable, self.script) + args,
+                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                universal_newlines=True)
+        out, err = proc.communicate(source)
+        self.assertIsNone(err)
+        return out
+
+    def lstriplines(self, data):
+        return '\n'.join(line.lstrip() for line in data.splitlines()) + '\n'
+
+    def test_selftest(self):
+        self.maxDiff = None
+        with temp_dir() as directory:
+            data_path = os.path.join(directory, '_test.py')
+            with open(self.script) as f:
+                closed = f.read()
+            with open(data_path, 'w') as f:
+                f.write(closed)
+
+            rc, out, err = assert_python_ok(self.script, '-d', data_path)
+            self.assertEqual(out, b'')
+            self.assertEqual(err, b'')
+            backup = data_path + '~'
+            self.assertTrue(os.path.exists(backup))
+            with open(backup) as f:
+                self.assertEqual(f.read(), closed)
+            with open(data_path) as f:
+                clean = f.read()
+            compile(clean, '_test.py', 'exec')
+            self.assertEqual(self.pindent(clean, '-c'), closed)
+            self.assertEqual(self.pindent(closed, '-d'), clean)
+
+            rc, out, err = assert_python_ok(self.script, '-c', data_path)
+            self.assertEqual(out, b'')
+            self.assertEqual(err, b'')
+            with open(backup) as f:
+                self.assertEqual(f.read(), clean)
+            with open(data_path) as f:
+                self.assertEqual(f.read(), closed)
+
+            broken = self.lstriplines(closed)
+            with open(data_path, 'w') as f:
+                f.write(broken)
+            rc, out, err = assert_python_ok(self.script, '-r', data_path)
+            self.assertEqual(out, b'')
+            self.assertEqual(err, b'')
+            with open(backup) as f:
+                self.assertEqual(f.read(), broken)
+            with open(data_path) as f:
+                indented = f.read()
+            compile(indented, '_test.py', 'exec')
+            self.assertEqual(self.pindent(broken, '-r'), indented)
+
+    def pindent_test(self, clean, closed):
+        self.assertEqual(self.pindent(clean, '-c'), closed)
+        self.assertEqual(self.pindent(closed, '-d'), clean)
+        broken = self.lstriplines(closed)
+        self.assertEqual(self.pindent(broken, '-r', '-e', '-s', '4'), closed)
+
+    def test_statements(self):
+        clean = textwrap.dedent("""\
+            if a:
+                pass
+
+            if a:
+                pass
+            else:
+                pass
+
+            if a:
+                pass
+            elif:
+                pass
+            else:
+                pass
+
+            while a:
+                break
+
+            while a:
+                break
+            else:
+                pass
+
+            for i in a:
+                break
+
+            for i in a:
+                break
+            else:
+                pass
+
+            try:
+                pass
+            finally:
+                pass
+
+            try:
+                pass
+            except TypeError:
+                pass
+            except ValueError:
+                pass
+            else:
+                pass
+
+            try:
+                pass
+            except TypeError:
+                pass
+            except ValueError:
+                pass
+            finally:
+                pass
+
+            with a:
+                pass
+
+            class A:
+                pass
+
+            def f():
+                pass
+            """)
+
+        closed = textwrap.dedent("""\
+            if a:
+                pass
+            # end if
+
+            if a:
+                pass
+            else:
+                pass
+            # end if
+
+            if a:
+                pass
+            elif:
+                pass
+            else:
+                pass
+            # end if
+
+            while a:
+                break
+            # end while
+
+            while a:
+                break
+            else:
+                pass
+            # end while
+
+            for i in a:
+                break
+            # end for
+
+            for i in a:
+                break
+            else:
+                pass
+            # end for
+
+            try:
+                pass
+            finally:
+                pass
+            # end try
+
+            try:
+                pass
+            except TypeError:
+                pass
+            except ValueError:
+                pass
+            else:
+                pass
+            # end try
+
+            try:
+                pass
+            except TypeError:
+                pass
+            except ValueError:
+                pass
+            finally:
+                pass
+            # end try
+
+            with a:
+                pass
+            # end with
+
+            class A:
+                pass
+            # end class A
+
+            def f():
+                pass
+            # end def f
+            """)
+        self.pindent_test(clean, closed)
+
+    def test_multilevel(self):
+        clean = textwrap.dedent("""\
+            def foobar(a, b):
+                if a == b:
+                    a = a+1
+                elif a < b:
+                    b = b-1
+                    if b > a: a = a-1
+                else:
+                    print 'oops!'
+            """)
+        closed = textwrap.dedent("""\
+            def foobar(a, b):
+                if a == b:
+                    a = a+1
+                elif a < b:
+                    b = b-1
+                    if b > a: a = a-1
+                    # end if
+                else:
+                    print 'oops!'
+                # end if
+            # end def foobar
+            """)
+        self.pindent_test(clean, closed)
+
+    def test_preserve_indents(self):
+        clean = textwrap.dedent("""\
+            if a:
+                     if b:
+                              pass
+            """)
+        closed = textwrap.dedent("""\
+            if a:
+                     if b:
+                              pass
+                     # end if
+            # end if
+            """)
+        self.assertEqual(self.pindent(clean, '-c'), closed)
+        self.assertEqual(self.pindent(closed, '-d'), clean)
+        broken = self.lstriplines(closed)
+        self.assertEqual(self.pindent(broken, '-r', '-e', '-s', '9'), closed)
+        clean = textwrap.dedent("""\
+            if a:
+            \tif b:
+            \t\tpass
+            """)
+        closed = textwrap.dedent("""\
+            if a:
+            \tif b:
+            \t\tpass
+            \t# end if
+            # end if
+            """)
+        self.assertEqual(self.pindent(clean, '-c'), closed)
+        self.assertEqual(self.pindent(closed, '-d'), clean)
+        broken = self.lstriplines(closed)
+        self.assertEqual(self.pindent(broken, '-r'), closed)
+
+    def test_escaped_newline(self):
+        clean = textwrap.dedent("""\
+            class\\
+            \\
+             A:
+               def\
+            \\
+            f:
+                  pass
+            """)
+        closed = textwrap.dedent("""\
+            class\\
+            \\
+             A:
+               def\
+            \\
+            f:
+                  pass
+               # end def f
+            # end class A
+            """)
+        self.assertEqual(self.pindent(clean, '-c'), closed)
+        self.assertEqual(self.pindent(closed, '-d'), clean)
+
+    def test_empty_line(self):
+        clean = textwrap.dedent("""\
+            if a:
+
+                pass
+            """)
+        closed = textwrap.dedent("""\
+            if a:
+
+                pass
+            # end if
+            """)
+        self.pindent_test(clean, closed)
+
+    def test_oneline(self):
+        clean = textwrap.dedent("""\
+            if a: pass
+            """)
+        closed = textwrap.dedent("""\
+            if a: pass
+            # end if
+            """)
+        self.pindent_test(clean, closed)
+
+
 def test_main():
-    test_support.run_unittest(ReindentTests)
+    test_support.run_unittest(*[obj for obj in globals().values()
+                                    if isinstance(obj, type)])
 
 
 if __name__ == '__main__':
diff --git a/lib-python/2.7/test/test_ucn.py b/lib-python/2.7/test/test_ucn.py
--- a/lib-python/2.7/test/test_ucn.py
+++ b/lib-python/2.7/test/test_ucn.py
@@ -8,6 +8,8 @@
 """#"
 
 import unittest
+import sys
+import _testcapi
 
 from test import test_support
 
@@ -137,6 +139,27 @@
             unicode, "\\NSPACE", 'unicode-escape', 'strict'
         )
 
+    @unittest.skipUnless(_testcapi.INT_MAX < _testcapi.PY_SSIZE_T_MAX,
+                         "needs UINT_MAX < SIZE_MAX")
+    @unittest.skipUnless(_testcapi.UINT_MAX < sys.maxint,
+                         "needs UINT_MAX < sys.maxint")
+    @test_support.bigmemtest(minsize=_testcapi.UINT_MAX + 1,
+                             memuse=2 + 4 // len(u'\U00010000'))
+    def test_issue16335(self, size):
+        func = self.test_issue16335
+        if size < func.minsize:
+            raise unittest.SkipTest("not enough memory: %.1fG minimum needed" %
+                    (func.minsize * func.memuse / float(1024**3),))
+        # very very long bogus character name
+        x = b'\\N{SPACE' + b'x' * int(_testcapi.UINT_MAX + 1) + b'}'
+        self.assertEqual(len(x), len(b'\\N{SPACE}') +
+                                    (_testcapi.UINT_MAX + 1))
+        self.assertRaisesRegexp(UnicodeError,
+            'unknown Unicode character name',
+            x.decode, 'unicode-escape'
+        )
+
+
 def test_main():
     test_support.run_unittest(UnicodeNamesTest)
 
diff --git a/lib-python/2.7/test/test_unicode.py b/lib-python/2.7/test/test_unicode.py
--- a/lib-python/2.7/test/test_unicode.py
+++ b/lib-python/2.7/test/test_unicode.py
@@ -644,6 +644,18 @@
                 return u'\u1234'
         self.assertEqual('%s' % Wrapper(), u'\u1234')
 
+    @test_support.cpython_only
+    def test_formatting_huge_precision(self):
+        from _testcapi import INT_MAX
+        format_string = u"%.{}f".format(INT_MAX + 1)
+        with self.assertRaises(ValueError):
+            result = format_string % 2.34
+
+    def test_formatting_huge_width(self):
+        format_string = u"%{}f".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format_string % 2.34
+
     def test_startswith_endswith_errors(self):
         for meth in (u'foo'.startswith, u'foo'.endswith):
             with self.assertRaises(UnicodeDecodeError):
@@ -1556,6 +1568,21 @@
         #  will fail
         self.assertRaises(UnicodeEncodeError, "foo{0}".format, u'\u1000bar')
 
+    def test_format_huge_precision(self):
+        format_string = u".{}f".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format(2.34, format_string)
+
+    def test_format_huge_width(self):
+        format_string = u"{}f".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format(2.34, format_string)
+
+    def test_format_huge_item_number(self):
+        format_string = u"{{{}:.6f}}".format(sys.maxsize + 1)
+        with self.assertRaises(ValueError):
+            result = format_string.format(2.34)
+
     def test_format_auto_numbering(self):
         class C:
             def __init__(self, x=100):
diff --git a/lib-python/2.7/test/test_urllib.py b/lib-python/2.7/test/test_urllib.py
--- a/lib-python/2.7/test/test_urllib.py
+++ b/lib-python/2.7/test/test_urllib.py
@@ -222,6 +222,27 @@
         finally:
             self.unfakehttp()
 
+    def test_missing_localfile(self):
+        self.assertRaises(IOError, urllib.urlopen,
+                'file://localhost/a/missing/file.py')
+        fd, tmp_file = tempfile.mkstemp()
+        tmp_fileurl = 'file://localhost/' + tmp_file.replace(os.path.sep, '/')
+        try:
+            self.assertTrue(os.path.exists(tmp_file))
+            fp = urllib.urlopen(tmp_fileurl)
+        finally:
+            os.close(fd)
+            fp.close()
+        os.unlink(tmp_file)
+
+        self.assertFalse(os.path.exists(tmp_file))
+        self.assertRaises(IOError, urllib.urlopen, tmp_fileurl)
+
+    def test_ftp_nonexisting(self):
+        self.assertRaises(IOError, urllib.urlopen,
+                'ftp://localhost/not/existing/file.py')
+
+
     def test_userpass_inurl(self):
         self.fakehttp('Hello!')
         try:
diff --git a/lib-python/2.7/test/test_urllib2.py b/lib-python/2.7/test/test_urllib2.py
--- a/lib-python/2.7/test/test_urllib2.py
+++ b/lib-python/2.7/test/test_urllib2.py
@@ -1106,12 +1106,30 @@
         self._test_basic_auth(opener, auth_handler, "Authorization",
                               realm, http_handler, password_manager,
                               "http://acme.example.com/protected",
-                              "http://acme.example.com/protected",
-                              )
+                              "http://acme.example.com/protected"
+                             )
 
     def test_basic_auth_with_single_quoted_realm(self):
         self.test_basic_auth(quote_char="'")
 
+    def test_basic_auth_with_unquoted_realm(self):
+        opener = OpenerDirector()
+        password_manager = MockPasswordManager()
+        auth_handler = urllib2.HTTPBasicAuthHandler(password_manager)
+        realm = "ACME Widget Store"
+        http_handler = MockHTTPHandler(
+            401, 'WWW-Authenticate: Basic realm=%s\r\n\r\n' % realm)
+        opener.add_handler(auth_handler)
+        opener.add_handler(http_handler)
+        msg = "Basic Auth Realm was unquoted"
+        with test_support.check_warnings((msg, UserWarning)):
+            self._test_basic_auth(opener, auth_handler, "Authorization",
+                                  realm, http_handler, password_manager,
+                                  "http://acme.example.com/protected",
+                                  "http://acme.example.com/protected"
+                                 )
+
+
     def test_proxy_basic_auth(self):
         opener = OpenerDirector()
         ph = urllib2.ProxyHandler(dict(http="proxy.example.com:3128"))
@@ -1130,7 +1148,7 @@
                               )
 
     def test_basic_and_digest_auth_handlers(self):
-        # HTTPDigestAuthHandler threw an exception if it couldn't handle a 40*
+        # HTTPDigestAuthHandler raised an exception if it couldn't handle a 40*
         # response (http://python.org/sf/1479302), where it should instead
         # return None to allow another handler (especially
         # HTTPBasicAuthHandler) to handle the response.
@@ -1318,16 +1336,32 @@
         req = Request(url)
         self.assertEqual(req.get_full_url(), url)
 
-def test_HTTPError_interface():
-    """
-    Issue 13211 reveals that HTTPError didn't implement the URLError
-    interface even though HTTPError is a subclass of URLError.
+    def test_HTTPError_interface(self):
+        """
+        Issue 13211 reveals that HTTPError didn't implement the URLError
+        interface even though HTTPError is a subclass of URLError.
 
-    >>> err = urllib2.HTTPError(msg='something bad happened', url=None, code=None, hdrs=None, fp=None)
-    >>> assert hasattr(err, 'reason')
-    >>> err.reason
-    'something bad happened'
-    """
+        >>> err = urllib2.HTTPError(msg='something bad happened', url=None, code=None, hdrs=None, fp=None)
+        >>> assert hasattr(err, 'reason')
+        >>> err.reason
+        'something bad happened'
+        """
+
+    def test_HTTPError_interface_call(self):
+        """
+        Issue 15701= - HTTPError interface has info method available from URLError.
+        """
+        err = urllib2.HTTPError(msg='something bad happened', url=None,
+                                code=None, hdrs='Content-Length:42', fp=None)
+        self.assertTrue(hasattr(err, 'reason'))
+        assert hasattr(err, 'reason')
+        assert hasattr(err, 'info')
+        assert callable(err.info)
+        try:
+            err.info()
+        except AttributeError:
+            self.fail("err.info() failed")
+        self.assertEqual(err.info(), "Content-Length:42")
 
 def test_main(verbose=None):
     from test import test_urllib2
diff --git a/lib-python/2.7/test/test_urllib2_localnet.py b/lib-python/2.7/test/test_urllib2_localnet.py
--- a/lib-python/2.7/test/test_urllib2_localnet.py
+++ b/lib-python/2.7/test/test_urllib2_localnet.py
@@ -5,7 +5,9 @@
 import BaseHTTPServer
 import unittest
 import hashlib
+
 from test import test_support
+
 mimetools = test_support.import_module('mimetools', deprecated=True)
 threading = test_support.import_module('threading')
 
@@ -346,6 +348,12 @@
     for transparent redirection have been written.
     """
 
+    def setUp(self):
+        proxy_handler = urllib2.ProxyHandler({})
+        opener = urllib2.build_opener(proxy_handler)
+        urllib2.install_opener(opener)
+        super(TestUrlopen, self).setUp()
+
     def start_server(self, responses):
         handler = GetRequestHandler(responses)
 
diff --git a/lib-python/2.7/test/test_urllib2net.py b/lib-python/2.7/test/test_urllib2net.py
--- a/lib-python/2.7/test/test_urllib2net.py
+++ b/lib-python/2.7/test/test_urllib2net.py
@@ -157,12 +157,12 @@
 ##             self._test_urls(urls, self._extra_handlers()+[bauth, dauth])
 
     def test_urlwithfrag(self):
-        urlwith_frag = "http://docs.python.org/glossary.html#glossary"
+        urlwith_frag = "http://docs.python.org/2/glossary.html#glossary"
         with test_support.transient_internet(urlwith_frag):
             req = urllib2.Request(urlwith_frag)
             res = urllib2.urlopen(req)
             self.assertEqual(res.geturl(),
-                    "http://docs.python.org/glossary.html#glossary")
+                    "http://docs.python.org/2/glossary.html#glossary")
 
     def test_fileno(self):
         req = urllib2.Request("http://www.python.org")
diff --git a/lib-python/2.7/test/test_urlparse.py b/lib-python/2.7/test/test_urlparse.py
--- a/lib-python/2.7/test/test_urlparse.py
+++ b/lib-python/2.7/test/test_urlparse.py
@@ -437,6 +437,51 @@
         self.assertEqual(p.port, 80)
         self.assertEqual(p.geturl(), url)
 
+        # Verify an illegal port of value greater than 65535 is set as None
+        url = "http://www.python.org:65536"
+        p = urlparse.urlsplit(url)
+        self.assertEqual(p.port, None)
+
+    def test_issue14072(self):
+        p1 = urlparse.urlsplit('tel:+31-641044153')
+        self.assertEqual(p1.scheme, 'tel')
+        self.assertEqual(p1.path, '+31-641044153')
+
+        p2 = urlparse.urlsplit('tel:+31641044153')
+        self.assertEqual(p2.scheme, 'tel')
+        self.assertEqual(p2.path, '+31641044153')
+
+        # Assert for urlparse
+        p1 = urlparse.urlparse('tel:+31-641044153')
+        self.assertEqual(p1.scheme, 'tel')
+        self.assertEqual(p1.path, '+31-641044153')
+
+        p2 = urlparse.urlparse('tel:+31641044153')
+        self.assertEqual(p2.scheme, 'tel')
+        self.assertEqual(p2.path, '+31641044153')
+
+
+    def test_telurl_params(self):
+        p1 = urlparse.urlparse('tel:123-4;phone-context=+1-650-516')
+        self.assertEqual(p1.scheme, 'tel')
+        self.assertEqual(p1.path, '123-4')
+        self.assertEqual(p1.params, 'phone-context=+1-650-516')
+
+        p1 = urlparse.urlparse('tel:+1-201-555-0123')
+        self.assertEqual(p1.scheme, 'tel')
+        self.assertEqual(p1.path, '+1-201-555-0123')
+        self.assertEqual(p1.params, '')
+
+        p1 = urlparse.urlparse('tel:7042;phone-context=example.com')
+        self.assertEqual(p1.scheme, 'tel')
+        self.assertEqual(p1.path, '7042')
+        self.assertEqual(p1.params, 'phone-context=example.com')
+
+        p1 = urlparse.urlparse('tel:863-1234;phone-context=+1-914-555')
+        self.assertEqual(p1.scheme, 'tel')
+        self.assertEqual(p1.path, '863-1234')
+        self.assertEqual(p1.params, 'phone-context=+1-914-555')
+
 
     def test_attributes_bad_port(self):
         """Check handling of non-integer ports."""
@@ -493,6 +538,10 @@
                          ('s3','foo.com','/stuff','','',''))
         self.assertEqual(urlparse.urlparse("x-newscheme://foo.com/stuff"),
                          ('x-newscheme','foo.com','/stuff','','',''))
+        self.assertEqual(urlparse.urlparse("x-newscheme://foo.com/stuff?query#fragment"),
+                         ('x-newscheme','foo.com','/stuff','','query','fragment'))
+        self.assertEqual(urlparse.urlparse("x-newscheme://foo.com/stuff?query"),
+                         ('x-newscheme','foo.com','/stuff','','query',''))
 
     def test_withoutscheme(self):
         # Test urlparse without scheme
diff --git a/lib-python/2.7/test/test_uu.py b/lib-python/2.7/test/test_uu.py
--- a/lib-python/2.7/test/test_uu.py
+++ b/lib-python/2.7/test/test_uu.py
@@ -48,7 +48,7 @@
         out = cStringIO.StringIO()
         try:
             uu.decode(inp, out)
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except uu.Error, e:
             self.assertEqual(str(e), "Truncated input file")
 
@@ -57,7 +57,7 @@
         out = cStringIO.StringIO()
         try:
             uu.decode(inp, out)
-            self.fail("No exception thrown")
+            self.fail("No exception raised")
         except uu.Error, e:
             self.assertEqual(str(e), "No valid begin line found in input file")
 
diff --git a/lib-python/2.7/test/test_weakref.py b/lib-python/2.7/test/test_weakref.py
--- a/lib-python/2.7/test/test_weakref.py
+++ b/lib-python/2.7/test/test_weakref.py
@@ -33,6 +33,27 @@
     return C.method
 
 
+class Object:
+    def __init__(self, arg):
+        self.arg = arg
+    def __repr__(self):
+        return "<Object %r>" % self.arg
+    def __eq__(self, other):
+        if isinstance(other, Object):
+            return self.arg == other.arg
+        return NotImplemented
+    def __ne__(self, other):
+        if isinstance(other, Object):
+            return self.arg != other.arg
+        return NotImplemented
+    def __hash__(self):
+        return hash(self.arg)
+
+class RefCycle:
+    def __init__(self):
+        self.cycle = self
+
+
 class TestBase(unittest.TestCase):
 
     def setUp(self):
@@ -705,6 +726,75 @@
         self.assertEqual(b(), None)
         self.assertEqual(l, [a, b])
 
+    def test_equality(self):
+        # Alive weakrefs defer equality testing to their underlying object.
+        x = Object(1)
+        y = Object(1)
+        z = Object(2)
+        a = weakref.ref(x)
+        b = weakref.ref(y)
+        c = weakref.ref(z)
+        d = weakref.ref(x)
+        # Note how we directly test the operators here, to stress both
+        # __eq__ and __ne__.
+        self.assertTrue(a == b)
+        self.assertFalse(a != b)
+        self.assertFalse(a == c)
+        self.assertTrue(a != c)
+        self.assertTrue(a == d)
+        self.assertFalse(a != d)
+        del x, y, z
+        gc.collect()
+        for r in a, b, c:
+            # Sanity check
+            self.assertIs(r(), None)
+        # Dead weakrefs compare by identity: whether `a` and `d` are the
+        # same weakref object is an implementation detail, since they pointed
+        # to the same original object and didn't have a callback.
+        # (see issue #16453).
+        self.assertFalse(a == b)
+        self.assertTrue(a != b)
+        self.assertFalse(a == c)
+        self.assertTrue(a != c)
+        self.assertEqual(a == d, a is d)
+        self.assertEqual(a != d, a is not d)
+
+    def test_hashing(self):
+        # Alive weakrefs hash the same as the underlying object
+        x = Object(42)
+        y = Object(42)
+        a = weakref.ref(x)
+        b = weakref.ref(y)
+        self.assertEqual(hash(a), hash(42))
+        del x, y
+        gc.collect()
+        # Dead weakrefs:
+        # - retain their hash is they were hashed when alive;
+        # - otherwise, cannot be hashed.
+        self.assertEqual(hash(a), hash(42))
+        self.assertRaises(TypeError, hash, b)
+
+    def test_trashcan_16602(self):
+        # Issue #16602: when a weakref's target was part of a long
+        # deallocation chain, the trashcan mechanism could delay clearing
+        # of the weakref and make the target object visible from outside
+        # code even though its refcount had dropped to 0.  A crash ensued.
+        class C(object):
+            def __init__(self, parent):
+                if not parent:
+                    return
+                wself = weakref.ref(self)
+                def cb(wparent):
+                    o = wself()
+                self.wparent = weakref.ref(parent, cb)
+
+        d = weakref.WeakKeyDictionary()
+        root = c = C(None)
+        for n in range(100):
+            d[c] = c = C(c)
+        del root
+        gc.collect()
+
 
 class SubclassableWeakrefTestCase(TestBase):
 
@@ -809,17 +899,6 @@
         self.assertEqual(self.cbcalled, 0)
 
 
-class Object:
-    def __init__(self, arg):
-        self.arg = arg
-    def __repr__(self):
-        return "<Object %r>" % self.arg
-
-class RefCycle:
-    def __init__(self):
-        self.cycle = self
-
-
 class MappingTestCase(TestBase):
 
     COUNT = 10
diff --git a/lib-python/2.7/test/test_winreg.py b/lib-python/2.7/test/test_winreg.py
--- a/lib-python/2.7/test/test_winreg.py
+++ b/lib-python/2.7/test/test_winreg.py
@@ -1,7 +1,7 @@
 # Test the windows specific win32reg module.
 # Only win32reg functions not hit here: FlushKey, LoadKey and SaveKey
 
-import os, sys
+import os, sys, errno
 import unittest
 from test import test_support
 threading = test_support.import_module("threading")
@@ -234,7 +234,7 @@
 
     def test_changing_value(self):
         # Issue2810: A race condition in 2.6 and 3.1 may cause
-        # EnumValue or QueryValue to throw "WindowsError: More data is
+        # EnumValue or QueryValue to raise "WindowsError: More data is
         # available"
         done = False
 
@@ -267,7 +267,7 @@
 
     def test_long_key(self):
         # Issue2810, in 2.6 and 3.1 when the key name was exactly 256
-        # characters, EnumKey threw "WindowsError: More data is
+        # characters, EnumKey raised "WindowsError: More data is
         # available"
         name = 'x'*256
         try:
@@ -282,8 +282,14 @@
 
     def test_dynamic_key(self):
         # Issue2810, when the value is dynamically generated, these
-        # throw "WindowsError: More data is available" in 2.6 and 3.1
-        EnumValue(HKEY_PERFORMANCE_DATA, 0)
+        # raise "WindowsError: More data is available" in 2.6 and 3.1
+        try:
+            EnumValue(HKEY_PERFORMANCE_DATA, 0)
+        except OSError as e:
+            if e.errno in (errno.EPERM, errno.EACCES):
+                self.skipTest("access denied to registry key "
+                              "(are you running in a non-interactive session?)")
+            raise
         QueryValueEx(HKEY_PERFORMANCE_DATA, None)
 
     # Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff
@@ -308,6 +314,35 @@
         finally:
             DeleteKey(HKEY_CURRENT_USER, test_key_name)
 
+    def test_setvalueex_value_range(self):
+        # Test for Issue #14420, accept proper ranges for SetValueEx.
+        # Py2Reg, which gets called by SetValueEx, was using PyLong_AsLong,
+        # thus raising OverflowError. The implementation now uses
+        # PyLong_AsUnsignedLong to match DWORD's size.
+        try:
+            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
+                self.assertNotEqual(ck.handle, 0)
+                SetValueEx(ck, "test_name", None, REG_DWORD, 0x80000000)
+        finally:
+            DeleteKey(HKEY_CURRENT_USER, test_key_name)
+
+    def test_queryvalueex_return_value(self):
+        # Test for Issue #16759, return unsigned int from QueryValueEx.
+        # Reg2Py, which gets called by QueryValueEx, was returning a value
+        # generated by PyLong_FromLong. The implmentation now uses
+        # PyLong_FromUnsignedLong to match DWORD's size.
+        try:
+            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
+                self.assertNotEqual(ck.handle, 0)
+                test_val = 0x80000000
+                SetValueEx(ck, "test_name", None, REG_DWORD, test_val)
+                ret_val, ret_type = QueryValueEx(ck, "test_name")
+                self.assertEqual(ret_type, REG_DWORD)
+                self.assertEqual(ret_val, test_val)
+        finally:
+            DeleteKey(HKEY_CURRENT_USER, test_key_name)
+
+
 
 @unittest.skipUnless(REMOTE_NAME, "Skipping remote registry tests")
 class RemoteWinregTests(BaseWinregTests):
diff --git a/lib-python/2.7/test/test_winsound.py b/lib-python/2.7/test/test_winsound.py
--- a/lib-python/2.7/test/test_winsound.py
+++ b/lib-python/2.7/test/test_winsound.py
@@ -2,6 +2,7 @@
 
 import unittest
 from test import test_support
+test_support.requires('audio')
 import time
 import os
 import subprocess
diff --git a/lib-python/2.7/test/test_wsgiref.py b/lib-python/2.7/test/test_wsgiref.py
--- a/lib-python/2.7/test/test_wsgiref.py
+++ b/lib-python/2.7/test/test_wsgiref.py
@@ -39,9 +39,6 @@
         pass
 
 
-
-
-
 def hello_app(environ,start_response):
     start_response("200 OK", [
         ('Content-Type','text/plain'),
@@ -62,27 +59,6 @@
     return out.getvalue(), err.getvalue()
 
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 def compare_generic_iter(make_it,match):
     """Utility to compare a generic 2.1/2.2+ iterator with an iterable
 
@@ -120,10 +96,6 @@
             raise AssertionError("Too many items from .next()",it)
 
 
-
-
-
-
 class IntegrationTests(TestCase):
 
     def check_hello(self, out, has_length=True):
@@ -161,10 +133,6 @@
         )
 
 
-
-
-
-
 class UtilityTests(TestCase):
 
     def checkShift(self,sn_in,pi_in,part,sn_out,pi_out):
@@ -201,11 +169,6 @@
         util.setup_testing_defaults(kw)
         self.assertEqual(util.request_uri(kw,query),uri)
 
-
-
-
-
-
     def checkFW(self,text,size,match):
 
         def make_it(text=text,size=size):
@@ -224,7 +187,6 @@
         it.close()
         self.assertTrue(it.filelike.closed)
 
-
     def testSimpleShifts(self):
         self.checkShift('','/', '', '/', '')
         self.checkShift('','/x', 'x', '/x', '')
@@ -232,7 +194,6 @@
         self.checkShift('/a','/x/y', 'x', '/a/x', '/y')
         self.checkShift('/a','/x/',  'x', '/a/x', '/')
 
-
     def testNormalizedShifts(self):
         self.checkShift('/a/b', '/../y', '..', '/a', '/y')
         self.checkShift('', '/../y', '..', '', '/y')
@@ -246,7 +207,6 @@
         self.checkShift('/a/b', '/x//', 'x', '/a/b/x', '/')
         self.checkShift('/a/b', '/.', None, '/a/b', '')
 
-
     def testDefaults(self):
         for key, value in [
             ('SERVER_NAME','127.0.0.1'),
@@ -266,7 +226,6 @@
         ]:
             self.checkDefault(key,value)
 
-
     def testCrossDefaults(self):
         self.checkCrossDefault('HTTP_HOST',"foo.bar",SERVER_NAME="foo.bar")
         self.checkCrossDefault('wsgi.url_scheme',"https",HTTPS="on")
@@ -276,7 +235,6 @@
         self.checkCrossDefault('SERVER_PORT',"80",HTTPS="foo")
         self.checkCrossDefault('SERVER_PORT',"443",HTTPS="on")
 
-
     def testGuessScheme(self):
         self.assertEqual(util.guess_scheme({}), "http")
         self.assertEqual(util.guess_scheme({'HTTPS':"foo"}), "http")
@@ -284,10 +242,6 @@
         self.assertEqual(util.guess_scheme({'HTTPS':"yes"}), "https")
         self.assertEqual(util.guess_scheme({'HTTPS':"1"}), "https")
 
-
-
-
-
     def testAppURIs(self):
         self.checkAppURI("http://127.0.0.1/")
         self.checkAppURI("http://127.0.0.1/spam", SCRIPT_NAME="/spam")
@@ -411,15 +365,6 @@
         raise   # for testing, we want to see what's happening
 
 
-
-
-
-
-
-
-
-
-
 class HandlerTests(TestCase):
 
     def checkEnvironAttrs(self, handler):
@@ -460,7 +405,6 @@
         h=TestHandler(); h.setup_environ()
         self.assertEqual(h.environ['wsgi.url_scheme'],'http')
 
-
     def testAbstractMethods(self):
         h = BaseHandler()
         for name in [
@@ -469,7 +413,6 @@
             self.assertRaises(NotImplementedError, getattr(h,name))
         self.assertRaises(NotImplementedError, h._write, "test")
 
-
     def testContentLength(self):
         # Demo one reason iteration is better than write()...  ;)
 
@@ -549,7 +492,6 @@
             "\r\n"+MSG)
         self.assertNotEqual(h.stderr.getvalue().find("AssertionError"), -1)
 
-
     def testHeaderFormats(self):
 
         def non_error_app(e,s):
@@ -591,40 +533,28 @@
                             (stdpat%(version,sw), h.stdout.getvalue())
                         )
 
-# This epilogue is needed for compatibility with the Python 2.5 regrtest module
+    def testCloseOnError(self):
+        side_effects = {'close_called': False}
+        MSG = b"Some output has been sent"
+        def error_app(e,s):
+            s("200 OK",[])(MSG)
+            class CrashyIterable(object):
+                def __iter__(self):
+                    while True:
+                        yield b'blah'
+                        raise AssertionError("This should be caught by handler")
+
+                def close(self):
+                    side_effects['close_called'] = True
+            return CrashyIterable()
+
+        h = ErrorHandler()
+        h.run(error_app)
+        self.assertEqual(side_effects['close_called'], True)
+
 
 def test_main():
     test_support.run_unittest(__name__)
 
 if __name__ == "__main__":
     test_main()
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-# the above lines intentionally left blank
diff --git a/lib-python/2.7/test/test_xml_etree.py b/lib-python/2.7/test/test_xml_etree.py
--- a/lib-python/2.7/test/test_xml_etree.py
+++ b/lib-python/2.7/test/test_xml_etree.py
@@ -1822,6 +1822,26 @@
 
     """
 
+def check_html_empty_elems_serialization(self):
+    # issue 15970
+    # from http://www.w3.org/TR/html401/index/elements.html
+    """
+
+    >>> empty_elems = ['AREA', 'BASE', 'BASEFONT', 'BR', 'COL', 'FRAME', 'HR',
+    ...                'IMG', 'INPUT', 'ISINDEX', 'LINK', 'META', 'PARAM']
+    >>> elems = ''.join('<%s />' % elem for elem in empty_elems)
+    >>> serialize(ET.XML('<html>%s</html>' % elems), method='html')
+    '<html><AREA><BASE><BASEFONT><BR><COL><FRAME><HR><IMG><INPUT><ISINDEX><LINK><META><PARAM></html>'
+    >>> serialize(ET.XML('<html>%s</html>' % elems.lower()), method='html')
+    '<html><area><base><basefont><br><col><frame><hr><img><input><isindex><link><meta><param></html>'
+    >>> elems = ''.join('<%s></%s>' % (elem, elem) for elem in empty_elems)
+    >>> serialize(ET.XML('<html>%s</html>' % elems), method='html')
+    '<html><AREA><BASE><BASEFONT><BR><COL><FRAME><HR><IMG><INPUT><ISINDEX><LINK><META><PARAM></html>'
+    >>> serialize(ET.XML('<html>%s</html>' % elems.lower()), method='html')
+    '<html><area><base><basefont><br><col><frame><hr><img><input><isindex><link><meta><param></html>'
+
+    """
+
 # --------------------------------------------------------------------
 
 
diff --git a/lib-python/2.7/test/test_xrange.py b/lib-python/2.7/test/test_xrange.py
--- a/lib-python/2.7/test/test_xrange.py
+++ b/lib-python/2.7/test/test_xrange.py
@@ -46,6 +46,28 @@
                 self.fail('{}: wrong element at position {};'
                           'expected {}, got {}'.format(test_id, i, y, x))
 
+    def assert_xranges_equivalent(self, x, y):
+        # Check that two xrange objects are equivalent, in the sense of the
+        # associated sequences being the same.  We want to use this for large
+        # xrange objects, so instead of converting to lists and comparing
+        # directly we do a number of indirect checks.
+        if len(x) != len(y):
+            self.fail('{} and {} have different '
+                      'lengths: {} and {} '.format(x, y, len(x), len(y)))
+        if len(x) >= 1:
+            if x[0] != y[0]:
+                self.fail('{} and {} have different initial '
+                          'elements: {} and {} '.format(x, y, x[0], y[0]))
+            if x[-1] != y[-1]:
+                self.fail('{} and {} have different final '
+                          'elements: {} and {} '.format(x, y, x[-1], y[-1]))
+        if len(x) >= 2:
+            x_step = x[1] - x[0]
+            y_step = y[1] - y[0]
+            if x_step != y_step:
+                self.fail('{} and {} have different step: '
+                          '{} and {} '.format(x, y, x_step, y_step))
+
     def test_xrange(self):
         self.assertEqual(list(xrange(3)), [0, 1, 2])
         self.assertEqual(list(xrange(1, 5)), [1, 2, 3, 4])
@@ -104,6 +126,59 @@
                 self.assertEqual(list(pickle.loads(pickle.dumps(r, proto))),
                                  list(r))
 
+        M = min(sys.maxint, sys.maxsize)
+        large_testcases = testcases + [
+            (0, M, 1),
+            (M, 0, -1),
+            (0, M, M - 1),
+            (M // 2, M, 1),
+            (0, -M, -1),
+            (0, -M, 1 - M),
+            (-M, M, 2),
+            (-M, M, 1024),
+            (-M, M, 10585),
+            (M, -M, -2),
+            (M, -M, -1024),
+            (M, -M, -10585),
+            ]
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            for t in large_testcases:
+                r = xrange(*t)
+                r_out = pickle.loads(pickle.dumps(r, proto))
+                self.assert_xranges_equivalent(r_out, r)
+
+    def test_repr(self):
+        # Check that repr of an xrange is a valid representation
+        # of that xrange.
+
+        # Valid xranges have at most min(sys.maxint, sys.maxsize) elements.
+        M = min(sys.maxint, sys.maxsize)
+
+        testcases = [
+            (13,),
+            (0, 11),
+            (-22, 10),
+            (20, 3, -1),
+            (13, 21, 3),
+            (-2, 2, 2),
+            (0, M, 1),
+            (M, 0, -1),
+            (0, M, M - 1),
+            (M // 2, M, 1),
+            (0, -M, -1),
+            (0, -M, 1 - M),
+            (-M, M, 2),
+            (-M, M, 1024),
+            (-M, M, 10585),
+            (M, -M, -2),
+            (M, -M, -1024),
+            (M, -M, -10585),
+            ]
+        for t in testcases:
+            r = xrange(*t)
+            r_out = eval(repr(r))
+            self.assert_xranges_equivalent(r, r_out)
+
     def test_range_iterators(self):
         # see issue 7298
         limits = [base + jiggle
diff --git a/lib-python/2.7/test/test_zipfile.py b/lib-python/2.7/test/test_zipfile.py
--- a/lib-python/2.7/test/test_zipfile.py
+++ b/lib-python/2.7/test/test_zipfile.py
@@ -26,7 +26,7 @@
 
 SMALL_TEST_DATA = [('_ziptest1', '1q2w3e4r5t'),
                    ('ziptest2dir/_ziptest2', 'qawsedrftg'),
-                   ('/ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),
+                   ('ziptest2dir/ziptest3dir/_ziptest3', 'azsxdcfvgb'),
                    ('ziptest2dir/ziptest3dir/ziptest4dir/_ziptest3', '6y7u8i9o0p')]
 
 
@@ -391,10 +391,7 @@
                 writtenfile = zipfp.extract(fpath)
 
                 # make sure it was written to the right place
-                if os.path.isabs(fpath):
-                    correctfile = os.path.join(os.getcwd(), fpath[1:])
-                else:
-                    correctfile = os.path.join(os.getcwd(), fpath)
+                correctfile = os.path.join(os.getcwd(), fpath)
                 correctfile = os.path.normpath(correctfile)
 
                 self.assertEqual(writtenfile, correctfile)
@@ -414,10 +411,7 @@
         with zipfile.ZipFile(TESTFN2, "r") as zipfp:
             zipfp.extractall()
             for fpath, fdata in SMALL_TEST_DATA:
-                if os.path.isabs(fpath):
-                    outfile = os.path.join(os.getcwd(), fpath[1:])
-                else:
-                    outfile = os.path.join(os.getcwd(), fpath)
+                outfile = os.path.join(os.getcwd(), fpath)
 
                 self.assertEqual(fdata, open(outfile, "rb").read())
                 os.remove(outfile)
@@ -425,6 +419,92 @@
         # remove the test file subdirectories
         shutil.rmtree(os.path.join(os.getcwd(), 'ziptest2dir'))
 
+    def check_file(self, filename, content):
+        self.assertTrue(os.path.isfile(filename))
+        with open(filename, 'rb') as f:
+            self.assertEqual(f.read(), content)
+
+    def test_extract_hackers_arcnames(self):
+        hacknames = [
+            ('../foo/bar', 'foo/bar'),
+            ('foo/../bar', 'foo/bar'),
+            ('foo/../../bar', 'foo/bar'),
+            ('foo/bar/..', 'foo/bar'),
+            ('./../foo/bar', 'foo/bar'),
+            ('/foo/bar', 'foo/bar'),
+            ('/foo/../bar', 'foo/bar'),
+            ('/foo/../../bar', 'foo/bar'),
+        ]
+        if os.path.sep == '\\':
+            hacknames.extend([
+                (r'..\foo\bar', 'foo/bar'),
+                (r'..\/foo\/bar', 'foo/bar'),
+                (r'foo/\..\/bar', 'foo/bar'),
+                (r'foo\/../\bar', 'foo/bar'),
+                (r'C:foo/bar', 'foo/bar'),
+                (r'C:/foo/bar', 'foo/bar'),
+                (r'C://foo/bar', 'foo/bar'),
+                (r'C:\foo\bar', 'foo/bar'),
+                (r'//conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),
+                (r'\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
+                (r'///conky/mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),
+                (r'\\\conky\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
+                (r'//conky//mountpoint/foo/bar', 'conky/mountpoint/foo/bar'),
+                (r'\\conky\\mountpoint\foo\bar', 'conky/mountpoint/foo/bar'),
+                (r'//?/C:/foo/bar', '_/C_/foo/bar'),
+                (r'\\?\C:\foo\bar', '_/C_/foo/bar'),
+                (r'C:/../C:/foo/bar', 'C_/foo/bar'),
+                (r'a:b\c<d>e|f"g?h*i', 'b/c_d_e_f_g_h_i'),
+                ('../../foo../../ba..r', 'foo/ba..r'),
+            ])
+        else:  # Unix
+            hacknames.extend([
+                ('//foo/bar', 'foo/bar'),
+                ('../../foo../../ba..r', 'foo../ba..r'),
+                (r'foo/..\bar', r'foo/..\bar'),
+            ])
+
+        for arcname, fixedname in hacknames:
+            content = b'foobar' + arcname.encode()
+            with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp:
+                zinfo = zipfile.ZipInfo()
+                # preserve backslashes
+                zinfo.filename = arcname
+                zinfo.external_attr = 0o600 << 16
+                zipfp.writestr(zinfo, content)
+
+            arcname = arcname.replace(os.sep, "/")
+            targetpath = os.path.join('target', 'subdir', 'subsub')
+            correctfile = os.path.join(targetpath, *fixedname.split('/'))
+
+            with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
+                writtenfile = zipfp.extract(arcname, targetpath)
+                self.assertEqual(writtenfile, correctfile,
+                                 msg="extract %r" % arcname)
+            self.check_file(correctfile, content)
+            shutil.rmtree('target')
+
+            with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
+                zipfp.extractall(targetpath)
+            self.check_file(correctfile, content)
+            shutil.rmtree('target')
+
+            correctfile = os.path.join(os.getcwd(), *fixedname.split('/'))
+
+            with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
+                writtenfile = zipfp.extract(arcname)
+                self.assertEqual(writtenfile, correctfile,
+                                 msg="extract %r" % arcname)
+            self.check_file(correctfile, content)
+            shutil.rmtree(fixedname.split('/')[0])
+
+            with zipfile.ZipFile(TESTFN2, 'r') as zipfp:
+                zipfp.extractall()
+            self.check_file(correctfile, content)
+            shutil.rmtree(fixedname.split('/')[0])
+
+            os.remove(TESTFN2)
+
     def test_writestr_compression(self):
         zipfp = zipfile.ZipFile(TESTFN2, "w")
         zipfp.writestr("a.txt", "hello world", compress_type=zipfile.ZIP_STORED)
@@ -760,6 +840,20 @@
         chk = zipfile.is_zipfile(fp)
         self.assertTrue(not chk)
 
+    def test_damaged_zipfile(self):
+        """Check that zipfiles with missing bytes at the end raise BadZipFile."""
+        # - Create a valid zip file
+        fp = io.BytesIO()
+        with zipfile.ZipFile(fp, mode="w") as zipf:
+            zipf.writestr("foo.txt", b"O, for a Muse of Fire!")
+        zipfiledata = fp.getvalue()
+
+        # - Now create copies of it missing the last N bytes and make sure
+        #   a BadZipFile exception is raised when we try to open it
+        for N in range(len(zipfiledata)):
+            fp = io.BytesIO(zipfiledata[:N])
+            self.assertRaises(zipfile.BadZipfile, zipfile.ZipFile, fp)
+
     def test_is_zip_valid_file(self):
         """Check that is_zipfile() correctly identifies zip files."""
         # - passing a filename
@@ -811,7 +905,7 @@
         with zipfile.ZipFile(data, mode="w") as zipf:
             zipf.writestr("foo.txt", "O, for a Muse of Fire!")
 
-        # This is correct; calling .read on a closed ZipFile should throw
+        # This is correct; calling .read on a closed ZipFile should raise
         # a RuntimeError, and so should calling .testzip.  An earlier
         # version of .testzip would swallow this exception (and any other)
         # and report that the first file in the archive was corrupt.
@@ -859,6 +953,17 @@
         caught."""
         self.assertRaises(RuntimeError, zipfile.ZipFile, TESTFN, "w", -1)
 
+    def test_unsupported_compression(self):
+        # data is declared as shrunk, but actually deflated
+        data = (b'PK\x03\x04.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00'
+        b'\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00x\x03\x00PK\x01'
+        b'\x02.\x03.\x00\x00\x00\x01\x00\xe4C\xa1@\x00\x00\x00\x00\x02\x00\x00'
+        b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+        b'\x80\x01\x00\x00\x00\x00xPK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00'
+        b'/\x00\x00\x00!\x00\x00\x00\x00\x00')
+        with zipfile.ZipFile(io.BytesIO(data), 'r') as zipf:
+            self.assertRaises(NotImplementedError, zipf.open, 'x')
+
     def test_null_byte_in_filename(self):
         """Check that a filename containing a null byte is properly
         terminated."""
@@ -908,6 +1013,22 @@
         with zipfile.ZipFile(TESTFN, mode="r") as zipf:
             self.assertEqual(zipf.comment, comment2)
 
+    def test_change_comment_in_empty_archive(self):
+        with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
+            self.assertFalse(zipf.filelist)
+            zipf.comment = b"this is a comment"
+        with zipfile.ZipFile(TESTFN, "r") as zipf:
+            self.assertEqual(zipf.comment, b"this is a comment")
+
+    def test_change_comment_in_nonempty_archive(self):
+        with zipfile.ZipFile(TESTFN, "w", zipfile.ZIP_STORED) as zipf:
+            zipf.writestr("foo.txt", "O, for a Muse of Fire!")
+        with zipfile.ZipFile(TESTFN, "a", zipfile.ZIP_STORED) as zipf:
+            self.assertTrue(zipf.filelist)
+            zipf.comment = b"this is a comment"
+        with zipfile.ZipFile(TESTFN, "r") as zipf:
+            self.assertEqual(zipf.comment, b"this is a comment")
+
     def check_testzip_with_bad_crc(self, compression):
         """Tests that files with bad CRCs return their name from testzip."""
         zipdata = self.zips_with_bad_crc[compression]
diff --git a/lib-python/2.7/test/test_zipimport_support.py b/lib-python/2.7/test/test_zipimport_support.py
--- a/lib-python/2.7/test/test_zipimport_support.py
+++ b/lib-python/2.7/test/test_zipimport_support.py
@@ -29,7 +29,8 @@
 #  test_cmd_line_script (covers the zipimport support in runpy)
 
 # Retrieve some helpers from other test cases
-from test import test_doctest, sample_doctest
+from test import (test_doctest, sample_doctest, sample_doctest_no_doctests,
+                  sample_doctest_no_docstrings)
 from test.test_importhooks import ImportHooksBaseTestCase
 
 
@@ -99,16 +100,26 @@
                                     "test_zipped_doctest")
         test_src = test_src.replace("test.sample_doctest",
                                     "sample_zipped_doctest")
-        sample_src = inspect.getsource(sample_doctest)
-        sample_src = sample_src.replace("test.test_doctest",
-                                        "test_zipped_doctest")
+        # The sample doctest files rewritten to include in the zipped version.
+        sample_sources = {}
+        for mod in [sample_doctest, sample_doctest_no_doctests,
+                    sample_doctest_no_docstrings]:
+            src = inspect.getsource(mod)
+            src = src.replace("test.test_doctest", "test_zipped_doctest")
+            # Rewrite the module name so that, for example,
+            # "test.sample_doctest" becomes "sample_zipped_doctest".
+            mod_name = mod.__name__.split(".")[-1]
+            mod_name = mod_name.replace("sample_", "sample_zipped_")
+            sample_sources[mod_name] = src
+
         with temp_dir() as d:
             script_name = make_script(d, 'test_zipped_doctest',
                                             test_src)
             zip_name, run_name = make_zip_script(d, 'test_zip',
                                                 script_name)
             z = zipfile.ZipFile(zip_name, 'a')
-            z.writestr("sample_zipped_doctest.py", sample_src)
+            for mod_name, src in sample_sources.items():
+                z.writestr(mod_name + ".py", src)
             z.close()
             if verbose:
                 zip_file = zipfile.ZipFile(zip_name, 'r')
@@ -168,9 +179,10 @@
                 test_zipped_doctest.test_unittest_reportflags,
             ]
             # Needed for test_DocTestParser and test_debug
-            deprecations = [
+            deprecations = []
+            if __debug__:
                 # Ignore all warnings about the use of class Tester in this module.
-                ("class Tester is deprecated", DeprecationWarning)]
+                deprecations.append(("class Tester is deprecated", DeprecationWarning))
             if sys.py3kwarning:
                 deprecations += [
                     ("backquote not supported", SyntaxWarning),
diff --git a/lib-python/2.7/test/test_zlib.py b/lib-python/2.7/test/test_zlib.py
--- a/lib-python/2.7/test/test_zlib.py
+++ b/lib-python/2.7/test/test_zlib.py
@@ -396,6 +396,18 @@
         y += dco.flush()
         self.assertEqual(y, 'foo')
 
+    def test_flush_with_freed_input(self):
+        # Issue #16411: decompressor accesses input to last decompress() call
+        # in flush(), even if this object has been freed in the meanwhile.
+        input1 = 'abcdefghijklmnopqrstuvwxyz'
+        input2 = 'QWERTYUIOPASDFGHJKLZXCVBNM'
+        data = zlib.compress(input1)
+        dco = zlib.decompressobj()
+        dco.decompress(data, 1)
+        del data
+        data = zlib.compress(input2)
+        self.assertEqual(dco.flush(), input1[1:])
+
     if hasattr(zlib.compressobj(), "copy"):
         def test_compresscopy(self):
             # Test copying a compression object
@@ -426,6 +438,31 @@
             c.flush()
             self.assertRaises(ValueError, c.copy)
 
+    def test_decompress_unused_data(self):
+        # Repeated calls to decompress() after EOF should accumulate data in
+        # dco.unused_data, instead of just storing the arg to the last call.
+        source = b'abcdefghijklmnopqrstuvwxyz'
+        remainder = b'0123456789'
+        y = zlib.compress(source)
+        x = y + remainder
+        for maxlen in 0, 1000:
+            for step in 1, 2, len(y), len(x):
+                dco = zlib.decompressobj()
+                data = b''
+                for i in range(0, len(x), step):
+                    if i < len(y):
+                        self.assertEqual(dco.unused_data, b'')
+                    if maxlen == 0:
+                        data += dco.decompress(x[i : i + step])
+                        self.assertEqual(dco.unconsumed_tail, b'')
+                    else:
+                        data += dco.decompress(
+                                dco.unconsumed_tail + x[i : i + step], maxlen)
+                data += dco.flush()
+                self.assertEqual(data, source)
+                self.assertEqual(dco.unconsumed_tail, b'')
+                self.assertEqual(dco.unused_data, remainder)
+
     if hasattr(zlib.decompressobj(), "copy"):
         def test_decompresscopy(self):
             # Test copying a decompression object
diff --git a/lib-python/2.7/test/testbz2_bigmem.bz2 b/lib-python/2.7/test/testbz2_bigmem.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..c9a4616c8053b1c92a45023635c8610c26299fc2
GIT binary patch
[stripped]
diff --git a/lib-python/2.7/test/testtar.tar b/lib-python/2.7/test/testtar.tar
index bac0e2628f35243f236db2fac82737882699b2f0..440182a437da84ef3d61d8cbc0f4209254615b37
GIT binary patch
[stripped]
diff --git a/lib-python/2.7/textwrap.py b/lib-python/2.7/textwrap.py
--- a/lib-python/2.7/textwrap.py
+++ b/lib-python/2.7/textwrap.py
@@ -9,6 +9,14 @@
 
 import string, re
 
+try:
+    _unicode = unicode
+except NameError:
+    # If Python is built without Unicode support, the unicode type
+    # will not exist. Fake one.
+    class _unicode(object):
+        pass
+
 # Do the right thing with boolean values for all known Python versions
 # (so this module can be copied to projects that don't depend on Python
 # 2.3, e.g. Optik and Docutils) by uncommenting the block of code below.
@@ -147,7 +155,7 @@
         if self.replace_whitespace:
             if isinstance(text, str):
                 text = text.translate(self.whitespace_trans)
-            elif isinstance(text, unicode):
+            elif isinstance(text, _unicode):
                 text = text.translate(self.unicode_whitespace_trans)
         return text
 
@@ -167,7 +175,7 @@
           'use', ' ', 'the', ' ', '-b', ' ', option!'
         otherwise.
         """
-        if isinstance(text, unicode):
+        if isinstance(text, _unicode):
             if self.break_on_hyphens:
                 pat = self.wordsep_re_uni
             else:
diff --git a/lib-python/2.7/threading.py b/lib-python/2.7/threading.py
--- a/lib-python/2.7/threading.py
+++ b/lib-python/2.7/threading.py
@@ -10,6 +10,7 @@
 
 import warnings
 
+from collections import deque as _deque
 from time import time as _time, sleep as _sleep
 from traceback import format_exc as _format_exc
 
@@ -605,6 +606,10 @@
                     pass
 
     def __stop(self):
+        # DummyThreads delete self.__block, but they have no waiters to
+        # notify anyway (join() is forbidden on them).
+        if not hasattr(self, '_Thread__block'):
+            return
         self.__block.acquire()
         self.__stopped = True
         self.__block.notify_all()
@@ -909,7 +914,7 @@
             self.rc = Condition(self.mon)
             self.wc = Condition(self.mon)
             self.limit = limit
-            self.queue = deque()
+            self.queue = _deque()
 
         def put(self, item):
             self.mon.acquire()
diff --git a/lib-python/2.7/tokenize.py b/lib-python/2.7/tokenize.py
--- a/lib-python/2.7/tokenize.py
+++ b/lib-python/2.7/tokenize.py
@@ -70,10 +70,10 @@
 Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''"
 # Tail end of """ string.
 Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
-Triple = group("[uU]?[rR]?'''", '[uU]?[rR]?"""')
+Triple = group("[uUbB]?[rR]?'''", '[uUbB]?[rR]?"""')
 # Single-line ' or " string.
-String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
-               r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"')
+String = group(r"[uUbB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
+               r'[uUbB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"')
 
 # Because of leftmost-then-longest match semantics, be sure to put the
 # longest operators first (e.g., if = came before ==, == would get
@@ -91,11 +91,11 @@
 Token = Ignore + PlainToken
 
 # First (or only) line of ' or " string.
-ContStr = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
+ContStr = group(r"[uUbB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
                 group("'", r'\\\r?\n'),
-                r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
+                r'[uUbB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
                 group('"', r'\\\r?\n'))
-PseudoExtras = group(r'\\\r?\n', Comment, Triple)
+PseudoExtras = group(r'\\\r?\n|\Z', Comment, Triple)
 PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
 
 tokenprog, pseudoprog, single3prog, double3prog = map(
@@ -362,6 +362,8 @@
             if pseudomatch:                                # scan for tokens
                 start, end = pseudomatch.span(1)
                 spos, epos, pos = (lnum, start), (lnum, end), end
+                if start == end:
+                    continue
                 token, initial = line[start:end], line[start]
 
                 if initial in numchars or \
diff --git a/lib-python/2.7/traceback.py b/lib-python/2.7/traceback.py
--- a/lib-python/2.7/traceback.py
+++ b/lib-python/2.7/traceback.py
@@ -166,7 +166,7 @@
     # >>> raise string1, string2  # deprecated
     #
     # Clear these out first because issubtype(string1, SyntaxError)
-    # would throw another exception and mask the original problem.
+    # would raise another exception and mask the original problem.
     if (isinstance(etype, BaseException) or
         isinstance(etype, types.InstanceType) or
         etype is None or type(etype) is str):
diff --git a/lib-python/2.7/unittest/case.py b/lib-python/2.7/unittest/case.py
--- a/lib-python/2.7/unittest/case.py
+++ b/lib-python/2.7/unittest/case.py
@@ -6,6 +6,7 @@
 import difflib
 import pprint
 import re
+import types
 import warnings
 
 from . import result
@@ -55,7 +56,7 @@
     Unconditionally skip a test.
     """
     def decorator(test_item):
-        if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
+        if not isinstance(test_item, (type, types.ClassType)):
             @functools.wraps(test_item)
             def skip_wrapper(*args, **kwargs):
                 raise SkipTest(reason)
@@ -201,7 +202,11 @@
         self.addTypeEqualityFunc(tuple, 'assertTupleEqual')
         self.addTypeEqualityFunc(set, 'assertSetEqual')
         self.addTypeEqualityFunc(frozenset, 'assertSetEqual')
-        self.addTypeEqualityFunc(unicode, 'assertMultiLineEqual')
+        try:
+            self.addTypeEqualityFunc(unicode, 'assertMultiLineEqual')
+        except NameError:
+            # No unicode support in this build
+            pass
 
     def addTypeEqualityFunc(self, typeobj, function):
         """Add a type specific assertEqual style function to compare a type.
@@ -442,10 +447,10 @@
 
 
     def assertRaises(self, excClass, callableObj=None, *args, **kwargs):
-        """Fail unless an exception of class excClass is thrown
+        """Fail unless an exception of class excClass is raised
            by callableObj when invoked with arguments args and keyword
            arguments kwargs. If a different type of exception is
-           thrown, it will not be caught, and the test case will be
+           raised, it will not be caught, and the test case will be
            deemed to have suffered an error, exactly as for an
            unexpected exception.
 
@@ -511,7 +516,7 @@
         assertion_func(first, second, msg=msg)
 
     def assertNotEqual(self, first, second, msg=None):
-        """Fail if the two objects are equal as determined by the '=='
+        """Fail if the two objects are equal as determined by the '!='
            operator.
         """
         if not first != second:
@@ -871,7 +876,7 @@
             - [0, 1, 1] and [1, 0, 1] compare equal.
             - [0, 0, 1] and [0, 1] compare unequal.
         """
-        first_seq, second_seq = list(actual_seq), list(expected_seq)
+        first_seq, second_seq = list(expected_seq), list(actual_seq)
         with warnings.catch_warnings():
             if sys.py3kwarning:
                 # Silence Py3k warning raised during the sorting
diff --git a/lib-python/2.7/unittest/main.py b/lib-python/2.7/unittest/main.py
--- a/lib-python/2.7/unittest/main.py
+++ b/lib-python/2.7/unittest/main.py
@@ -157,7 +157,10 @@
             self.test = self.testLoader.loadTestsFromNames(self.testNames,
                                                            self.module)
 
-    def _do_discovery(self, argv, Loader=loader.TestLoader):
+    def _do_discovery(self, argv, Loader=None):
+        if Loader is None:
+            Loader = lambda: self.testLoader
+
         # handle command line args for test discovery
         self.progName = '%s discover' % self.progName
         import optparse
diff --git a/lib-python/2.7/unittest/runner.py b/lib-python/2.7/unittest/runner.py
--- a/lib-python/2.7/unittest/runner.py
+++ b/lib-python/2.7/unittest/runner.py
@@ -34,7 +34,7 @@
     separator2 = '-' * 70
 
     def __init__(self, stream, descriptions, verbosity):
-        super(TextTestResult, self).__init__()
+        super(TextTestResult, self).__init__(stream, descriptions, verbosity)
         self.stream = stream
         self.showAll = verbosity > 1
         self.dots = verbosity == 1
diff --git a/lib-python/2.7/unittest/signals.py b/lib-python/2.7/unittest/signals.py
--- a/lib-python/2.7/unittest/signals.py
+++ b/lib-python/2.7/unittest/signals.py
@@ -9,6 +9,20 @@
 class _InterruptHandler(object):
     def __init__(self, default_handler):
         self.called = False
+        self.original_handler = default_handler
+        if isinstance(default_handler, int):
+            if default_handler == signal.SIG_DFL:
+                # Pretend it's signal.default_int_handler instead.
+                default_handler = signal.default_int_handler
+            elif default_handler == signal.SIG_IGN:
+                # Not quite the same thing as SIG_IGN, but the closest we
+                # can make it: do nothing.
+                def default_handler(unused_signum, unused_frame):
+                    pass
+            else:
+                raise TypeError("expected SIGINT signal handler to be "
+                                "signal.SIG_IGN, signal.SIG_DFL, or a "
+                                "callable object")
         self.default_handler = default_handler
 
     def __call__(self, signum, frame):
@@ -54,4 +68,4 @@
 
     global _interrupt_handler
     if _interrupt_handler is not None:
-        signal.signal(signal.SIGINT, _interrupt_handler.default_handler)
+        signal.signal(signal.SIGINT, _interrupt_handler.original_handler)
diff --git a/lib-python/2.7/unittest/test/test_break.py b/lib-python/2.7/unittest/test/test_break.py
--- a/lib-python/2.7/unittest/test/test_break.py
+++ b/lib-python/2.7/unittest/test/test_break.py
@@ -15,9 +15,12 @@
 @unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
     "if threads have been used")
 class TestBreak(unittest.TestCase):
+    int_handler = None
 
     def setUp(self):
         self._default_handler = signal.getsignal(signal.SIGINT)
+        if self.int_handler is not None:
+            signal.signal(signal.SIGINT, self.int_handler)
 
     def tearDown(self):
         signal.signal(signal.SIGINT, self._default_handler)
@@ -74,6 +77,10 @@
 
 
     def testSecondInterrupt(self):
+        # Can't use skipIf decorator because the signal handler may have
+        # been changed after defining this method.
+        if signal.getsignal(signal.SIGINT) == signal.SIG_IGN:
+            self.skipTest("test requires SIGINT to not be ignored")
         result = unittest.TestResult()
         unittest.installHandler()
         unittest.registerResult(result)
@@ -123,6 +130,10 @@
 
 
     def testHandlerReplacedButCalled(self):
+        # Can't use skipIf decorator because the signal handler may have
+        # been changed after defining this method.
+        if signal.getsignal(signal.SIGINT) == signal.SIG_IGN:
+            self.skipTest("test requires SIGINT to not be ignored")
         # If our handler has been replaced (is no longer installed) but is
         # called by the *new* handler, then it isn't safe to delay the
         # SIGINT and we should immediately delegate to the default handler
@@ -250,3 +261,24 @@
 
         test()
         self.assertNotEqual(signal.getsignal(signal.SIGINT), default_handler)
+
+ at unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill")
+ at unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows")
+ at unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
+    "if threads have been used")
+class TestBreakDefaultIntHandler(TestBreak):
+    int_handler = signal.default_int_handler
+
+ at unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill")
+ at unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows")
+ at unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
+    "if threads have been used")
+class TestBreakSignalIgnored(TestBreak):
+    int_handler = signal.SIG_IGN
+
+ at unittest.skipUnless(hasattr(os, 'kill'), "Test requires os.kill")
+ at unittest.skipIf(sys.platform =="win32", "Test cannot run on Windows")
+ at unittest.skipIf(sys.platform == 'freebsd6', "Test kills regrtest on freebsd6 "
+    "if threads have been used")
+class TestBreakSignalDefault(TestBreak):
+    int_handler = signal.SIG_DFL
diff --git a/lib-python/2.7/unittest/test/test_discovery.py b/lib-python/2.7/unittest/test/test_discovery.py
--- a/lib-python/2.7/unittest/test/test_discovery.py
+++ b/lib-python/2.7/unittest/test/test_discovery.py
@@ -220,12 +220,26 @@
 
         program = object.__new__(unittest.TestProgram)
         program.usageExit = usageExit
+        program.testLoader = None
 
         with self.assertRaises(Stop):
             # too many args
             program._do_discovery(['one', 'two', 'three', 'four'])
 
 
+    def test_command_line_handling_do_discovery_uses_default_loader(self):
+        program = object.__new__(unittest.TestProgram)
+
+        class Loader(object):
+            args = []
+            def discover(self, start_dir, pattern, top_level_dir):
+                self.args.append((start_dir, pattern, top_level_dir))
+                return 'tests'
+
+        program.testLoader = Loader()
+        program._do_discovery(['-v'])
+        self.assertEqual(Loader.args, [('.', 'test*.py', None)])
+
     def test_command_line_handling_do_discovery_calls_loader(self):
         program = object.__new__(unittest.TestProgram)
 
diff --git a/lib-python/2.7/unittest/test/test_runner.py b/lib-python/2.7/unittest/test/test_runner.py
--- a/lib-python/2.7/unittest/test/test_runner.py
+++ b/lib-python/2.7/unittest/test/test_runner.py
@@ -149,6 +149,19 @@
         self.assertEqual(runner.resultclass, unittest.TextTestResult)
 
 
+    def test_multiple_inheritance(self):
+        class AResult(unittest.TestResult):
+            def __init__(self, stream, descriptions, verbosity):
+                super(AResult, self).__init__(stream, descriptions, verbosity)
+
+        class ATextResult(unittest.TextTestResult, AResult):
+            pass
+
+        # This used to raise an exception due to TextTestResult not passing
+        # on arguments in its __init__ super call
+        ATextResult(None, None, None)
+
+
     def testBufferAndFailfast(self):
         class Test(unittest.TestCase):
             def testFoo(self):
diff --git a/lib-python/2.7/unittest/test/test_skipping.py b/lib-python/2.7/unittest/test/test_skipping.py
--- a/lib-python/2.7/unittest/test/test_skipping.py
+++ b/lib-python/2.7/unittest/test/test_skipping.py
@@ -66,6 +66,36 @@
         self.assertEqual(result.skipped, [(test, "testing")])
         self.assertEqual(record, [])
 
+    def test_skip_non_unittest_class_old_style(self):
+        @unittest.skip("testing")
+        class Mixin:
+            def test_1(self):
+                record.append(1)
+        class Foo(Mixin, unittest.TestCase):
+            pass
+        record = []
+        result = unittest.TestResult()
+        test = Foo("test_1")
+        suite = unittest.TestSuite([test])
+        suite.run(result)
+        self.assertEqual(result.skipped, [(test, "testing")])
+        self.assertEqual(record, [])
+
+    def test_skip_non_unittest_class_new_style(self):
+        @unittest.skip("testing")
+        class Mixin(object):
+            def test_1(self):
+                record.append(1)
+        class Foo(Mixin, unittest.TestCase):
+            pass
+        record = []
+        result = unittest.TestResult()
+        test = Foo("test_1")
+        suite = unittest.TestSuite([test])
+        suite.run(result)
+        self.assertEqual(result.skipped, [(test, "testing")])
+        self.assertEqual(record, [])
+
     def test_expected_failure(self):
         class Foo(unittest.TestCase):
             @unittest.expectedFailure
diff --git a/lib-python/2.7/urllib2.py b/lib-python/2.7/urllib2.py
--- a/lib-python/2.7/urllib2.py
+++ b/lib-python/2.7/urllib2.py
@@ -102,6 +102,7 @@
 import time
 import urlparse
 import bisect
+import warnings
 
 try:
     from cStringIO import StringIO
@@ -109,7 +110,7 @@
     from StringIO import StringIO
 
 from urllib import (unwrap, unquote, splittype, splithost, quote,
-     addinfourl, splitport, splittag,
+     addinfourl, splitport, splittag, toBytes,
      splitattr, ftpwrapper, splituser, splitpasswd, splitvalue)
 
 # support for FileHandler, proxies via environment variables
@@ -172,6 +173,9 @@
     def reason(self):
         return self.msg
 
+    def info(self):
+        return self.hdrs
+
 # copied from cookielib.py
 _cut_port_re = re.compile(r":\d+$")
 def request_host(request):
@@ -828,7 +832,7 @@
     # allow for double- and single-quoted realm values
     # (single quotes are a violation of the RFC, but appear in the wild)
     rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+'
-                    'realm=(["\'])(.*?)\\2', re.I)
+                    'realm=(["\']?)([^"\']*)\\2', re.I)
 
     # XXX could pre-emptively send auth info already accepted (RFC 2617,
     # end of section 2, and section 1.2 immediately after "credentials"
@@ -861,6 +865,9 @@
             mo = AbstractBasicAuthHandler.rx.search(authreq)
             if mo:
                 scheme, quote, realm = mo.groups()
+                if quote not in ['"', "'"]:
+                    warnings.warn("Basic Auth Realm was unquoted",
+                                  UserWarning, 2)
                 if scheme.lower() == 'basic':
                     response = self.retry_http_basic_auth(host, req, realm)
                     if response and response.code != 401:
diff --git a/lib-python/2.7/urlparse.py b/lib-python/2.7/urlparse.py
--- a/lib-python/2.7/urlparse.py
+++ b/lib-python/2.7/urlparse.py
@@ -40,11 +40,14 @@
                'imap', 'wais', 'file', 'mms', 'https', 'shttp',
                'snews', 'prospero', 'rtsp', 'rtspu', 'rsync', '',
                'svn', 'svn+ssh', 'sftp','nfs','git', 'git+ssh']
+uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap',
+               'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips',
+               'mms', '', 'sftp', 'tel']
+
+# These are not actually used anymore, but should stay for backwards
+# compatibility.  (They are undocumented, but have a public-looking name.)
 non_hierarchical = ['gopher', 'hdl', 'mailto', 'news',
                     'telnet', 'wais', 'imap', 'snews', 'sip', 'sips']
-uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap',
-               'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips',
-               'mms', '', 'sftp']
 uses_query = ['http', 'wais', 'imap', 'https', 'shttp', 'mms',
               'gopher', 'rtsp', 'rtspu', 'sip', 'sips', '']
 uses_fragment = ['ftp', 'hdl', 'http', 'gopher', 'news',
@@ -104,9 +107,11 @@
         netloc = self.netloc.split('@')[-1].split(']')[-1]
         if ':' in netloc:
             port = netloc.split(':')[1]
-            return int(port, 10)
-        else:
-            return None
+            port = int(port, 10)
+            # verify legal port
+            if (0 <= port <= 65535):
+                return port
+        return None
 
 from collections import namedtuple
 
@@ -192,21 +197,21 @@
             if c not in scheme_chars:
                 break
         else:
-            try:
-                # make sure "url" is not actually a port number (in which case
-                # "scheme" is really part of the path
-                _testportnum = int(url[i+1:])
-            except ValueError:
-                scheme, url = url[:i].lower(), url[i+1:]
+            # make sure "url" is not actually a port number (in which case
+            # "scheme" is really part of the path)
+            rest = url[i+1:]
+            if not rest or any(c not in '0123456789' for c in rest):
+                # not a port number
+                scheme, url = url[:i].lower(), rest
 
     if url[:2] == '//':
         netloc, url = _splitnetloc(url, 2)
         if (('[' in netloc and ']' not in netloc) or
                 (']' in netloc and '[' not in netloc)):
             raise ValueError("Invalid IPv6 URL")
-    if allow_fragments and scheme in uses_fragment and '#' in url:
+    if allow_fragments and '#' in url:
         url, fragment = url.split('#', 1)
-    if scheme in uses_query and '?' in url:
+    if '?' in url:
         url, query = url.split('?', 1)
     v = SplitResult(scheme, netloc, url, query, fragment)
     _parse_cache[key] = v
diff --git a/lib-python/2.7/wave.py b/lib-python/2.7/wave.py
--- a/lib-python/2.7/wave.py
+++ b/lib-python/2.7/wave.py
@@ -261,9 +261,9 @@
     #
 
     def _read_fmt_chunk(self, chunk):
-        wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack('<hhllh', chunk.read(14))
+        wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack('<HHLLH', chunk.read(14))
         if wFormatTag == WAVE_FORMAT_PCM:
-            sampwidth = struct.unpack('<h', chunk.read(2))[0]
+            sampwidth = struct.unpack('<H', chunk.read(2))[0]
             self._sampwidth = (sampwidth + 7) // 8
         else:
             raise Error, 'unknown format: %r' % (wFormatTag,)
@@ -466,14 +466,14 @@
             self._nframes = initlength / (self._nchannels * self._sampwidth)
         self._datalength = self._nframes * self._nchannels * self._sampwidth
         self._form_length_pos = self._file.tell()
-        self._file.write(struct.pack('<l4s4slhhllhh4s',
+        self._file.write(struct.pack('<L4s4sLHHLLHH4s',
             36 + self._datalength, 'WAVE', 'fmt ', 16,
             WAVE_FORMAT_PCM, self._nchannels, self._framerate,
             self._nchannels * self._framerate * self._sampwidth,
             self._nchannels * self._sampwidth,
             self._sampwidth * 8, 'data'))
         self._data_length_pos = self._file.tell()
-        self._file.write(struct.pack('<l', self._datalength))
+        self._file.write(struct.pack('<L', self._datalength))
         self._headerwritten = True
 
     def _patchheader(self):
@@ -482,9 +482,9 @@
             return
         curpos = self._file.tell()
         self._file.seek(self._form_length_pos, 0)
-        self._file.write(struct.pack('<l', 36 + self._datawritten))
+        self._file.write(struct.pack('<L', 36 + self._datawritten))
         self._file.seek(self._data_length_pos, 0)
-        self._file.write(struct.pack('<l', self._datawritten))
+        self._file.write(struct.pack('<L', self._datawritten))
         self._file.seek(curpos, 0)
         self._datalength = self._datawritten
 
diff --git a/lib-python/2.7/wsgiref/handlers.py b/lib-python/2.7/wsgiref/handlers.py
--- a/lib-python/2.7/wsgiref/handlers.py
+++ b/lib-python/2.7/wsgiref/handlers.py
@@ -122,11 +122,13 @@
         in the event loop to iterate over the data, and to call
         'self.close()' once the response is finished.
         """
-        if not self.result_is_file() or not self.sendfile():
-            for data in self.result:
-                self.write(data)
-            self.finish_content()
-        self.close()
+        try:
+            if not self.result_is_file() or not self.sendfile():
+                for data in self.result:
+                    self.write(data)
+                self.finish_content()
+        finally:
+            self.close()
 
 
     def get_scheme(self):
diff --git a/lib-python/2.7/wsgiref/validate.py b/lib-python/2.7/wsgiref/validate.py
--- a/lib-python/2.7/wsgiref/validate.py
+++ b/lib-python/2.7/wsgiref/validate.py
@@ -134,9 +134,9 @@
     When applied between a WSGI server and a WSGI application, this
     middleware will check for WSGI compliancy on a number of levels.
     This middleware does not modify the request or response in any
-    way, but will throw an AssertionError if anything seems off
+    way, but will raise an AssertionError if anything seems off
     (except for a failure to close the application iterator, which
-    will be printed to stderr -- there's no way to throw an exception
+    will be printed to stderr -- there's no way to raise an exception
     at that point).
     """
 
diff --git a/lib-python/2.7/xml/dom/minidom.py b/lib-python/2.7/xml/dom/minidom.py
--- a/lib-python/2.7/xml/dom/minidom.py
+++ b/lib-python/2.7/xml/dom/minidom.py
@@ -1,5 +1,6 @@
-"""\
-minidom.py -- a lightweight DOM implementation.
+"""Simple implementation of the Level 1 DOM.
+
+Namespaces and other minor Level 2 features are also supported.
 
 parse("foo.xml")
 
diff --git a/lib-python/2.7/xml/etree/ElementTree.py b/lib-python/2.7/xml/etree/ElementTree.py
--- a/lib-python/2.7/xml/etree/ElementTree.py
+++ b/lib-python/2.7/xml/etree/ElementTree.py
@@ -779,11 +779,12 @@
     # @param file A file name, or a file object opened for writing.
     # @param **options Options, given as keyword arguments.
     # @keyparam encoding Optional output encoding (default is US-ASCII).
-    # @keyparam method Optional output method ("xml", "html", "text" or
-    #     "c14n"; default is "xml").
     # @keyparam xml_declaration Controls if an XML declaration should
     #     be added to the file.  Use False for never, True for always,
     #     None for only if not US-ASCII or UTF-8.  None is default.
+    # @keyparam default_namespace Sets the default XML namespace (for "xmlns").
+    # @keyparam method Optional output method ("xml", "html", "text" or
+    #     "c14n"; default is "xml").
 
     def write(self, file_or_filename,
               # keyword arguments
@@ -945,7 +946,7 @@
         write(_escape_cdata(elem.tail, encoding))
 
 HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr",
-              "img", "input", "isindex", "link", "meta" "param")
+              "img", "input", "isindex", "link", "meta", "param")
 
 try:
     HTML_EMPTY = set(HTML_EMPTY)
diff --git a/lib-python/2.7/xml/sax/_exceptions.py b/lib-python/2.7/xml/sax/_exceptions.py
--- a/lib-python/2.7/xml/sax/_exceptions.py
+++ b/lib-python/2.7/xml/sax/_exceptions.py
@@ -12,7 +12,7 @@
     the application: you can subclass it to provide additional
     functionality, or to add localization. Note that although you will
     receive a SAXException as the argument to the handlers in the
-    ErrorHandler interface, you are not actually required to throw
+    ErrorHandler interface, you are not actually required to raise
     the exception; instead, you can simply read the information in
     it."""
 
@@ -50,7 +50,7 @@
     the original XML document. Note that although the application will
     receive a SAXParseException as the argument to the handlers in the
     ErrorHandler interface, the application is not actually required
-    to throw the exception; instead, it can simply read the
+    to raise the exception; instead, it can simply read the
     information in it and take a different action.
 
     Since this exception is a subclass of SAXException, it inherits
@@ -62,7 +62,7 @@
         self._locator = locator
 
         # We need to cache this stuff at construction time.
-        # If this exception is thrown, the objects through which we must
+        # If this exception is raised, the objects through which we must
         # traverse to get this information may be deleted by the time
         # it gets caught.
         self._systemId = self._locator.getSystemId()
diff --git a/lib-python/2.7/xml/sax/expatreader.py b/lib-python/2.7/xml/sax/expatreader.py
--- a/lib-python/2.7/xml/sax/expatreader.py
+++ b/lib-python/2.7/xml/sax/expatreader.py
@@ -108,7 +108,10 @@
 
     def prepareParser(self, source):
         if source.getSystemId() is not None:
-            self._parser.SetBase(source.getSystemId())
+            base = source.getSystemId()
+            if isinstance(base, unicode):
+                base = base.encode('utf-8')
+            self._parser.SetBase(base)
 
     # Redefined setContentHandler to allow changing handlers during parsing
 
diff --git a/lib-python/2.7/xml/sax/saxutils.py b/lib-python/2.7/xml/sax/saxutils.py
--- a/lib-python/2.7/xml/sax/saxutils.py
+++ b/lib-python/2.7/xml/sax/saxutils.py
@@ -4,6 +4,8 @@
 """
 
 import os, urlparse, urllib, types
+import io
+import sys
 import handler
 import xmlreader
 
@@ -12,15 +14,6 @@
 except AttributeError:
     _StringTypes = [types.StringType]
 
-# See whether the xmlcharrefreplace error handler is
-# supported
-try:
-    from codecs import xmlcharrefreplace_errors
-    _error_handling = "xmlcharrefreplace"
-    del xmlcharrefreplace_errors
-except ImportError:
-    _error_handling = "strict"
-
 def __dict_replace(s, d):
     """Replace substrings of a string using a dictionary."""
     for key, value in d.items():
@@ -81,25 +74,50 @@
     return data
 
 
+def _gettextwriter(out, encoding):
+    if out is None:
+        import sys
+        out = sys.stdout
+
+    if isinstance(out, io.RawIOBase):
+        buffer = io.BufferedIOBase(out)
+        # Keep the original file open when the TextIOWrapper is
+        # destroyed
+        buffer.close = lambda: None
+    else:
+        # This is to handle passed objects that aren't in the
+        # IOBase hierarchy, but just have a write method
+        buffer = io.BufferedIOBase()
+        buffer.writable = lambda: True
+        buffer.write = out.write
+        try:
+            # TextIOWrapper uses this methods to determine
+            # if BOM (for UTF-16, etc) should be added
+            buffer.seekable = out.seekable
+            buffer.tell = out.tell
+        except AttributeError:
+            pass
+    # wrap a binary writer with TextIOWrapper
+    class UnbufferedTextIOWrapper(io.TextIOWrapper):
+        def write(self, s):
+            super(UnbufferedTextIOWrapper, self).write(s)
+            self.flush()
+    return UnbufferedTextIOWrapper(buffer, encoding=encoding,
+                                   errors='xmlcharrefreplace',
+                                   newline='\n')
+
 class XMLGenerator(handler.ContentHandler):
 
     def __init__(self, out=None, encoding="iso-8859-1"):
-        if out is None:
-            import sys
-            out = sys.stdout
         handler.ContentHandler.__init__(self)
-        self._out = out
+        out = _gettextwriter(out, encoding)
+        self._write = out.write
+        self._flush = out.flush
         self._ns_contexts = [{}] # contains uri -> prefix dicts
         self._current_context = self._ns_contexts[-1]
         self._undeclared_ns_maps = []
         self._encoding = encoding
 
-    def _write(self, text):
-        if isinstance(text, str):
-            self._out.write(text)
-        else:
-            self._out.write(text.encode(self._encoding, _error_handling))
-
     def _qname(self, name):
         """Builds a qualified name from a (ns_url, localname) pair"""
         if name[0]:
@@ -120,9 +138,12 @@
     # ContentHandler methods
 
     def startDocument(self):
-        self._write('<?xml version="1.0" encoding="%s"?>\n' %
+        self._write(u'<?xml version="1.0" encoding="%s"?>\n' %
                         self._encoding)
 
+    def endDocument(self):
+        self._flush()
+
     def startPrefixMapping(self, prefix, uri):
         self._ns_contexts.append(self._current_context.copy())
         self._current_context[uri] = prefix
@@ -133,39 +154,39 @@
         del self._ns_contexts[-1]
 
     def startElement(self, name, attrs):
-        self._write('<' + name)
+        self._write(u'<' + name)
         for (name, value) in attrs.items():
-            self._write(' %s=%s' % (name, quoteattr(value)))
-        self._write('>')
+            self._write(u' %s=%s' % (name, quoteattr(value)))
+        self._write(u'>')
 
     def endElement(self, name):
-        self._write('</%s>' % name)
+        self._write(u'</%s>' % name)
 
     def startElementNS(self, name, qname, attrs):
-        self._write('<' + self._qname(name))
+        self._write(u'<' + self._qname(name))
 
         for prefix, uri in self._undeclared_ns_maps:
             if prefix:
-                self._out.write(' xmlns:%s="%s"' % (prefix, uri))
+                self._write(u' xmlns:%s="%s"' % (prefix, uri))
             else:
-                self._out.write(' xmlns="%s"' % uri)
+                self._write(u' xmlns="%s"' % uri)
         self._undeclared_ns_maps = []
 
         for (name, value) in attrs.items():
-            self._write(' %s=%s' % (self._qname(name), quoteattr(value)))
-        self._write('>')
+            self._write(u' %s=%s' % (self._qname(name), quoteattr(value)))
+        self._write(u'>')
 
     def endElementNS(self, name, qname):
-        self._write('</%s>' % self._qname(name))
+        self._write(u'</%s>' % self._qname(name))
 
     def characters(self, content):
-        self._write(escape(content))
+        self._write(escape(unicode(content)))
 
     def ignorableWhitespace(self, content):
-        self._write(content)
+        self._write(unicode(content))
 
     def processingInstruction(self, target, data):
-        self._write('<?%s %s?>' % (target, data))
+        self._write(u'<?%s %s?>' % (target, data))
 
 
 class XMLFilterBase(xmlreader.XMLReader):
@@ -293,14 +314,31 @@
             source.setSystemId(f.name)
 
     if source.getByteStream() is None:
-        sysid = source.getSystemId()
-        basehead = os.path.dirname(os.path.normpath(base))
-        sysidfilename = os.path.join(basehead, sysid)
-        if os.path.isfile(sysidfilename):
+        try:
+            sysid = source.getSystemId()
+            basehead = os.path.dirname(os.path.normpath(base))
+            encoding = sys.getfilesystemencoding()
+            if isinstance(sysid, unicode):
+                if not isinstance(basehead, unicode):
+                    try:
+                        basehead = basehead.decode(encoding)
+                    except UnicodeDecodeError:
+                        sysid = sysid.encode(encoding)
+            else:
+                if isinstance(basehead, unicode):
+                    try:
+                        sysid = sysid.decode(encoding)
+                    except UnicodeDecodeError:
+                        basehead = basehead.encode(encoding)
+            sysidfilename = os.path.join(basehead, sysid)
+            isfile = os.path.isfile(sysidfilename)
+        except UnicodeError:
+            isfile = False
+        if isfile:
             source.setSystemId(sysidfilename)
             f = open(sysidfilename, "rb")
         else:
-            source.setSystemId(urlparse.urljoin(base, sysid))
+            source.setSystemId(urlparse.urljoin(base, source.getSystemId()))
             f = urllib.urlopen(source.getSystemId())
 
         source.setByteStream(f)
diff --git a/lib-python/2.7/xml/sax/xmlreader.py b/lib-python/2.7/xml/sax/xmlreader.py
--- a/lib-python/2.7/xml/sax/xmlreader.py
+++ b/lib-python/2.7/xml/sax/xmlreader.py
@@ -68,7 +68,7 @@
 
         SAX parsers are not required to provide localization for errors
         and warnings; if they cannot support the requested locale,
-        however, they must throw a SAX exception. Applications may
+        however, they must raise a SAX exception. Applications may
         request a locale change in the middle of a parse."""
         raise SAXNotSupportedException("Locale support not implemented")
 
diff --git a/lib-python/2.7/xmlrpclib.py b/lib-python/2.7/xmlrpclib.py
--- a/lib-python/2.7/xmlrpclib.py
+++ b/lib-python/2.7/xmlrpclib.py
@@ -945,7 +945,7 @@
 
 class MultiCallIterator:
     """Iterates over the results of a multicall. Exceptions are
-    thrown in response to xmlrpc faults."""
+    raised in response to xmlrpc faults."""
 
     def __init__(self, results):
         self.results = results
diff --git a/lib-python/2.7/zipfile.py b/lib-python/2.7/zipfile.py
--- a/lib-python/2.7/zipfile.py
+++ b/lib-python/2.7/zipfile.py
@@ -5,6 +5,7 @@
 import binascii, cStringIO, stat
 import io
 import re
+import string
 
 try:
     import zlib # We may need its compression method
@@ -166,6 +167,8 @@
         return endrec
 
     data = fpin.read(sizeEndCentDir64Locator)
+    if len(data) != sizeEndCentDir64Locator:
+        return endrec
     sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
     if sig != stringEndArchive64Locator:
         return endrec
@@ -176,6 +179,8 @@
     # Assume no 'zip64 extensible data'
     fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2)
     data = fpin.read(sizeEndCentDir64)
+    if len(data) != sizeEndCentDir64:
+        return endrec
     sig, sz, create_version, read_version, disk_num, disk_dir, \
             dircount, dircount2, dirsize, diroffset = \
             struct.unpack(structEndArchive64, data)
@@ -211,7 +216,9 @@
     except IOError:
         return None
     data = fpin.read()
-    if data[0:4] == stringEndArchive and data[-2:] == "\000\000":
+    if (len(data) == sizeEndCentDir and
+        data[0:4] == stringEndArchive and
+        data[-2:] == b"\000\000"):
         # the signature is correct and there's no comment, unpack structure
         endrec = struct.unpack(structEndArchive, data)
         endrec=list(endrec)
@@ -235,6 +242,9 @@
     if start >= 0:
         # found the magic number; attempt to unpack and interpret
         recData = data[start:start+sizeEndCentDir]
+        if len(recData) != sizeEndCentDir:
+            # Zip file is corrupted.
+            return None
         endrec = list(struct.unpack(structEndArchive, recData))
         commentSize = endrec[_ECD_COMMENT_SIZE] #as claimed by the zip file
         comment = data[start+sizeEndCentDir:start+sizeEndCentDir+commentSize]
@@ -246,7 +256,7 @@
                              endrec)
 
     # Unable to find a valid end of central directory structure
-    return
+    return None
 
 
 class ZipInfo (object):
@@ -316,7 +326,7 @@
         # compress_size         Size of the compressed file
         # file_size             Size of the uncompressed file
 
-    def FileHeader(self):
+    def FileHeader(self, zip64=None):
         """Return the per-file header as a string."""
         dt = self.date_time
         dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
@@ -331,12 +341,17 @@
 
         extra = self.extra
 
-        if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT:
-            # File is larger than what fits into a 4 byte integer,
-            # fall back to the ZIP64 extension
+        if zip64 is None:
+            zip64 = file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT
+        if zip64:
             fmt = '<HHQQ'
             extra = extra + struct.pack(fmt,
                     1, struct.calcsize(fmt)-4, file_size, compress_size)
+        if file_size > ZIP64_LIMIT or compress_size > ZIP64_LIMIT:
+            if not zip64:
+                raise LargeZipFile("Filesize would require ZIP64 extensions")
+            # File is larger than what fits into a 4 byte integer,
+            # fall back to the ZIP64 extension
             file_size = 0xffffffff
             compress_size = 0xffffffff
             self.extract_version = max(45, self.extract_version)
@@ -461,6 +476,28 @@
         self._UpdateKeys(c)
         return c
 
+
+compressor_names = {
+    0: 'store',
+    1: 'shrink',
+    2: 'reduce',
+    3: 'reduce',
+    4: 'reduce',
+    5: 'reduce',
+    6: 'implode',
+    7: 'tokenize',
+    8: 'deflate',
+    9: 'deflate64',
+    10: 'implode',
+    12: 'bzip2',
+    14: 'lzma',
+    18: 'terse',
+    19: 'lz77',
+    97: 'wavpack',
+    98: 'ppmd',
+}
+
+
 class ZipExtFile(io.BufferedIOBase):
     """File-like object for reading an archive member.
        Is returned by ZipFile.open().
@@ -475,9 +512,11 @@
     # Search for universal newlines or line chunks.
     PATTERN = re.compile(r'^(?P<chunk>[^\r\n]+)|(?P<newline>\n|\r\n?)')
 
-    def __init__(self, fileobj, mode, zipinfo, decrypter=None):
+    def __init__(self, fileobj, mode, zipinfo, decrypter=None,
+            close_fileobj=False):
         self._fileobj = fileobj
         self._decrypter = decrypter
+        self._close_fileobj = close_fileobj
 
         self._compress_type = zipinfo.compress_type
         self._compress_size = zipinfo.compress_size
@@ -485,6 +524,12 @@
 
         if self._compress_type == ZIP_DEFLATED:
             self._decompressor = zlib.decompressobj(-15)
+        elif self._compress_type != ZIP_STORED:
+            descr = compressor_names.get(self._compress_type)
+            if descr:
+                raise NotImplementedError("compression type %d (%s)" % (self._compress_type, descr))
+            else:
+                raise NotImplementedError("compression type %d" % (self._compress_type,))
         self._unconsumed = ''
 
         self._readbuffer = ''
@@ -649,9 +694,15 @@
         self._offset += len(data)
         return data
 
+    def close(self):
+        try :
+            if self._close_fileobj:
+                self._fileobj.close()
+        finally:
+            super(ZipExtFile, self).close()
 
 
-class ZipFile:
+class ZipFile(object):
     """ Class with methods to open, read, write, close, list zip files.
 
     z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False)
@@ -690,7 +741,7 @@
         self.compression = compression  # Method of compression
         self.mode = key = mode.replace('b', '')[0]
         self.pwd = None
-        self.comment = ''
+        self._comment = ''
 
         # Check if we were passed a file-like object
         if isinstance(file, basestring):
@@ -710,30 +761,34 @@
             self.fp = file
             self.filename = getattr(file, 'name', None)
 
-        if key == 'r':
-            self._GetContents()
-        elif key == 'w':
-            # set the modified flag so central directory gets written
-            # even if no files are added to the archive
-            self._didModify = True
-        elif key == 'a':
-            try:
-                # See if file is a zip file
+        try:
+            if key == 'r':
                 self._RealGetContents()
-                # seek to start of directory and overwrite
-                self.fp.seek(self.start_dir, 0)
-            except BadZipfile:
-                # file is not a zip file, just append
-                self.fp.seek(0, 2)
-
+            elif key == 'w':
                 # set the modified flag so central directory gets written
                 # even if no files are added to the archive
                 self._didModify = True
-        else:
+            elif key == 'a':
+                try:
+                    # See if file is a zip file
+                    self._RealGetContents()
+                    # seek to start of directory and overwrite
+                    self.fp.seek(self.start_dir, 0)
+                except BadZipfile:
+                    # file is not a zip file, just append
+                    self.fp.seek(0, 2)
+
+                    # set the modified flag so central directory gets written
+                    # even if no files are added to the archive
+                    self._didModify = True
+            else:
+                raise RuntimeError('Mode must be "r", "w" or "a"')
+        except:
+            fp = self.fp
+            self.fp = None
             if not self._filePassed:
-                self.fp.close()
-                self.fp = None
-            raise RuntimeError, 'Mode must be "r", "w" or "a"'
+                fp.close()
+            raise
 
     def __enter__(self):
         return self
@@ -741,17 +796,6 @@
     def __exit__(self, type, value, traceback):
         self.close()
 
-    def _GetContents(self):
-        """Read the directory, making sure we close the file if the format
-        is bad."""
-        try:
-            self._RealGetContents()
-        except BadZipfile:
-            if not self._filePassed:
-                self.fp.close()
-                self.fp = None
-            raise
-
     def _RealGetContents(self):
         """Read in the table of contents for the ZIP file."""
         fp = self.fp
@@ -765,7 +809,7 @@
             print endrec
         size_cd = endrec[_ECD_SIZE]             # bytes in central directory
         offset_cd = endrec[_ECD_OFFSET]         # offset of central directory
-        self.comment = endrec[_ECD_COMMENT]     # archive comment
+        self._comment = endrec[_ECD_COMMENT]    # archive comment
 
         # "concat" is zero, unless zip was concatenated to another file
         concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
@@ -784,9 +828,11 @@
         total = 0
         while total < size_cd:
             centdir = fp.read(sizeCentralDir)
-            if centdir[0:4] != stringCentralDir:
-                raise BadZipfile, "Bad magic number for central directory"
+            if len(centdir) != sizeCentralDir:
+                raise BadZipfile("Truncated central directory")
             centdir = struct.unpack(structCentralDir, centdir)
+            if centdir[_CD_SIGNATURE] != stringCentralDir:
+                raise BadZipfile("Bad magic number for central directory")
             if self.debug > 2:
                 print centdir
             filename = fp.read(centdir[_CD_FILENAME_LENGTH])
@@ -845,9 +891,9 @@
             try:
                 # Read by chunks, to avoid an OverflowError or a
                 # MemoryError with very large embedded files.
-                f = self.open(zinfo.filename, "r")
-                while f.read(chunk_size):     # Check CRC-32
-                    pass
+                with self.open(zinfo.filename, "r") as f:
+                    while f.read(chunk_size):     # Check CRC-32
+                        pass
             except BadZipfile:
                 return zinfo.filename
 
@@ -864,6 +910,22 @@
         """Set default password for encrypted files."""
         self.pwd = pwd
 
+    @property
+    def comment(self):
+        """The comment text associated with the ZIP file."""
+        return self._comment
+
+    @comment.setter
+    def comment(self, comment):
+        # check for valid comment length
+        if len(comment) >= ZIP_MAX_COMMENT:
+            if self.debug:
+                print('Archive comment is too long; truncating to %d bytes'
+                        % ZIP_MAX_COMMENT)
+            comment = comment[:ZIP_MAX_COMMENT]
+        self._comment = comment
+        self._didModify = True
+
     def read(self, name, pwd=None):
         """Return file bytes (as a string) for name."""
         return self.open(name, "r", pwd).read()
@@ -880,62 +942,72 @@
         # given a file object in the constructor
         if self._filePassed:
             zef_file = self.fp
+            should_close = False
         else:
             zef_file = open(self.filename, 'rb')
+            should_close = True
 
-        # Make sure we have an info object
-        if isinstance(name, ZipInfo):
-            # 'name' is already an info object
-            zinfo = name
-        else:
-            # Get info object for name
-            zinfo = self.getinfo(name)
+        try:
+            # Make sure we have an info object
+            if isinstance(name, ZipInfo):
+                # 'name' is already an info object
+                zinfo = name
+            else:
+                # Get info object for name
+                zinfo = self.getinfo(name)
 
-        zef_file.seek(zinfo.header_offset, 0)
+            zef_file.seek(zinfo.header_offset, 0)
 
-        # Skip the file header:
-        fheader = zef_file.read(sizeFileHeader)
-        if fheader[0:4] != stringFileHeader:
-            raise BadZipfile, "Bad magic number for file header"
+            # Skip the file header:
+            fheader = zef_file.read(sizeFileHeader)
+            if len(fheader) != sizeFileHeader:
+                raise BadZipfile("Truncated file header")
+            fheader = struct.unpack(structFileHeader, fheader)
+            if fheader[_FH_SIGNATURE] != stringFileHeader:
+                raise BadZipfile("Bad magic number for file header")
 
-        fheader = struct.unpack(structFileHeader, fheader)
-        fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
-        if fheader[_FH_EXTRA_FIELD_LENGTH]:
-            zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
+            fname = zef_file.read(fheader[_FH_FILENAME_LENGTH])
+            if fheader[_FH_EXTRA_FIELD_LENGTH]:
+                zef_file.read(fheader[_FH_EXTRA_FIELD_LENGTH])
 
-        if fname != zinfo.orig_filename:
-            raise BadZipfile, \
-                      'File name in directory "%s" and header "%s" differ.' % (
-                          zinfo.orig_filename, fname)
+            if fname != zinfo.orig_filename:
+                raise BadZipfile, \
+                        'File name in directory "%s" and header "%s" differ.' % (
+                            zinfo.orig_filename, fname)
 
-        # check for encrypted flag & handle password
-        is_encrypted = zinfo.flag_bits & 0x1
-        zd = None
-        if is_encrypted:
-            if not pwd:
-                pwd = self.pwd
-            if not pwd:
-                raise RuntimeError, "File %s is encrypted, " \
-                      "password required for extraction" % name
+            # check for encrypted flag & handle password
+            is_encrypted = zinfo.flag_bits & 0x1
+            zd = None
+            if is_encrypted:
+                if not pwd:
+                    pwd = self.pwd
+                if not pwd:
+                    raise RuntimeError, "File %s is encrypted, " \
+                        "password required for extraction" % name
 
-            zd = _ZipDecrypter(pwd)
-            # The first 12 bytes in the cypher stream is an encryption header
-            #  used to strengthen the algorithm. The first 11 bytes are
-            #  completely random, while the 12th contains the MSB of the CRC,
-            #  or the MSB of the file time depending on the header type
-            #  and is used to check the correctness of the password.
-            bytes = zef_file.read(12)
-            h = map(zd, bytes[0:12])
-            if zinfo.flag_bits & 0x8:
-                # compare against the file type from extended local headers
-                check_byte = (zinfo._raw_time >> 8) & 0xff
-            else:
-                # compare against the CRC otherwise
-                check_byte = (zinfo.CRC >> 24) & 0xff
-            if ord(h[11]) != check_byte:
-                raise RuntimeError("Bad password for file", name)
+                zd = _ZipDecrypter(pwd)
+                # The first 12 bytes in the cypher stream is an encryption header
+                #  used to strengthen the algorithm. The first 11 bytes are
+                #  completely random, while the 12th contains the MSB of the CRC,
+                #  or the MSB of the file time depending on the header type
+                #  and is used to check the correctness of the password.
+                bytes = zef_file.read(12)
+                h = map(zd, bytes[0:12])
+                if zinfo.flag_bits & 0x8:
+                    # compare against the file type from extended local headers
+                    check_byte = (zinfo._raw_time >> 8) & 0xff
+                else:
+                    # compare against the CRC otherwise
+                    check_byte = (zinfo.CRC >> 24) & 0xff
+                if ord(h[11]) != check_byte:
+                    raise RuntimeError("Bad password for file", name)
 
-        return  ZipExtFile(zef_file, mode, zinfo, zd)
+            return ZipExtFile(zef_file, mode, zinfo, zd,
+                    close_fileobj=should_close)
+        except:
+            if should_close:
+                zef_file.close()
+            raise
 
     def extract(self, member, path=None, pwd=None):
         """Extract a member from the archive to the current working directory,
@@ -969,17 +1041,25 @@
         """
         # build the destination pathname, replacing
         # forward slashes to platform specific separators.
-        # Strip trailing path separator, unless it represents the root.
-        if (targetpath[-1:] in (os.path.sep, os.path.altsep)
-            and len(os.path.splitdrive(targetpath)[1]) > 1):
-            targetpath = targetpath[:-1]
+        arcname = member.filename.replace('/', os.path.sep)
 
-        # don't include leading "/" from file name if present
-        if member.filename[0] == '/':
-            targetpath = os.path.join(targetpath, member.filename[1:])
-        else:
-            targetpath = os.path.join(targetpath, member.filename)
+        if os.path.altsep:
+            arcname = arcname.replace(os.path.altsep, os.path.sep)
+        # interpret absolute pathname as relative, remove drive letter or
+        # UNC path, redundant separators, "." and ".." components.
+        arcname = os.path.splitdrive(arcname)[1]
+        arcname = os.path.sep.join(x for x in arcname.split(os.path.sep)
+                    if x not in ('', os.path.curdir, os.path.pardir))
+        if os.path.sep == '\\':
+            # filter illegal characters on Windows
+            illegal = ':<>|"?*'
+            table = string.maketrans(illegal, '_' * len(illegal))
+            arcname = arcname.translate(table)
+            # remove trailing dots
+            arcname = (x.rstrip('.') for x in arcname.split(os.path.sep))
+            arcname = os.path.sep.join(x for x in arcname if x)
 
+        targetpath = os.path.join(targetpath, arcname)
         targetpath = os.path.normpath(targetpath)
 
         # Create all upper directories if necessary.
@@ -992,11 +1072,9 @@
                 os.mkdir(targetpath)
             return targetpath
 
-        source = self.open(member, pwd=pwd)
-        target = file(targetpath, "wb")
-        shutil.copyfileobj(source, target)
-        source.close()
-        target.close()
+        with self.open(member, pwd=pwd) as source, \
+             file(targetpath, "wb") as target:
+            shutil.copyfileobj(source, target)
 
         return targetpath
 
@@ -1062,20 +1140,23 @@
             zinfo.CRC = 0
             self.filelist.append(zinfo)
             self.NameToInfo[zinfo.filename] = zinfo
-            self.fp.write(zinfo.FileHeader())
+            self.fp.write(zinfo.FileHeader(False))
             return
 
         with open(filename, "rb") as fp:
             # Must overwrite CRC and sizes with correct data later
             zinfo.CRC = CRC = 0
             zinfo.compress_size = compress_size = 0
-            zinfo.file_size = file_size = 0
-            self.fp.write(zinfo.FileHeader())
+            # Compressed size can be larger than uncompressed size
+            zip64 = self._allowZip64 and \
+                    zinfo.file_size * 1.05 > ZIP64_LIMIT
+            self.fp.write(zinfo.FileHeader(zip64))
             if zinfo.compress_type == ZIP_DEFLATED:
                 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
                      zlib.DEFLATED, -15)
             else:
                 cmpr = None
+            file_size = 0
             while 1:
                 buf = fp.read(1024 * 8)
                 if not buf:
@@ -1095,11 +1176,16 @@
             zinfo.compress_size = file_size
         zinfo.CRC = CRC
         zinfo.file_size = file_size
-        # Seek backwards and write CRC and file sizes
+        if not zip64 and self._allowZip64:
+            if file_size > ZIP64_LIMIT:
+                raise RuntimeError('File size has increased during compressing')
+            if compress_size > ZIP64_LIMIT:
+                raise RuntimeError('Compressed size larger than uncompressed size')
+        # Seek backwards and write file header (which will now include
+        # correct CRC and file sizes)
         position = self.fp.tell()       # Preserve current position in file
-        self.fp.seek(zinfo.header_offset + 14, 0)
-        self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
-              zinfo.file_size))
+        self.fp.seek(zinfo.header_offset, 0)
+        self.fp.write(zinfo.FileHeader(zip64))
         self.fp.seek(position, 0)
         self.filelist.append(zinfo)
         self.NameToInfo[zinfo.filename] = zinfo
@@ -1136,14 +1222,18 @@
             zinfo.compress_size = len(bytes)    # Compressed size
         else:
             zinfo.compress_size = zinfo.file_size
-        zinfo.header_offset = self.fp.tell()    # Start of header bytes
-        self.fp.write(zinfo.FileHeader())
+        zip64 = zinfo.file_size > ZIP64_LIMIT or \
+                zinfo.compress_size > ZIP64_LIMIT
+        if zip64 and not self._allowZip64:
+            raise LargeZipFile("Filesize would require ZIP64 extensions")
+        self.fp.write(zinfo.FileHeader(zip64))
         self.fp.write(bytes)
-        self.fp.flush()
         if zinfo.flag_bits & 0x08:
             # Write CRC and file sizes after the file data
-            self.fp.write(struct.pack("<LLL", zinfo.CRC, zinfo.compress_size,
+            fmt = '<LQQ' if zip64 else '<LLL'
+            self.fp.write(struct.pack(fmt, zinfo.CRC, zinfo.compress_size,
                   zinfo.file_size))
+        self.fp.flush()
         self.filelist.append(zinfo)
         self.NameToInfo[zinfo.filename] = zinfo
 
@@ -1157,109 +1247,104 @@
         if self.fp is None:
             return
 
-        if self.mode in ("w", "a") and self._didModify: # write ending records
-            count = 0
-            pos1 = self.fp.tell()
-            for zinfo in self.filelist:         # write central directory
-                count = count + 1
-                dt = zinfo.date_time
-                dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
-                dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
-                extra = []
-                if zinfo.file_size > ZIP64_LIMIT \
-                        or zinfo.compress_size > ZIP64_LIMIT:
-                    extra.append(zinfo.file_size)
-                    extra.append(zinfo.compress_size)
-                    file_size = 0xffffffff
-                    compress_size = 0xffffffff
-                else:
-                    file_size = zinfo.file_size
-                    compress_size = zinfo.compress_size
+        try:
+            if self.mode in ("w", "a") and self._didModify: # write ending records
+                count = 0
+                pos1 = self.fp.tell()
+                for zinfo in self.filelist:         # write central directory
+                    count = count + 1
+                    dt = zinfo.date_time
+                    dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
+                    dostime = dt[3] << 11 | dt[4] << 5 | (dt[5] // 2)
+                    extra = []
+                    if zinfo.file_size > ZIP64_LIMIT \
+                            or zinfo.compress_size > ZIP64_LIMIT:
+                        extra.append(zinfo.file_size)
+                        extra.append(zinfo.compress_size)
+                        file_size = 0xffffffff
+                        compress_size = 0xffffffff
+                    else:
+                        file_size = zinfo.file_size
+                        compress_size = zinfo.compress_size
 
-                if zinfo.header_offset > ZIP64_LIMIT:
-                    extra.append(zinfo.header_offset)
-                    header_offset = 0xffffffffL
-                else:
-                    header_offset = zinfo.header_offset
+                    if zinfo.header_offset > ZIP64_LIMIT:
+                        extra.append(zinfo.header_offset)
+                        header_offset = 0xffffffffL
+                    else:
+                        header_offset = zinfo.header_offset
 
-                extra_data = zinfo.extra
-                if extra:
-                    # Append a ZIP64 field to the extra's
-                    extra_data = struct.pack(
-                            '<HH' + 'Q'*len(extra),
-                            1, 8*len(extra), *extra) + extra_data
+                    extra_data = zinfo.extra
+                    if extra:
+                        # Append a ZIP64 field to the extra's
+                        extra_data = struct.pack(
+                                '<HH' + 'Q'*len(extra),
+                                1, 8*len(extra), *extra) + extra_data
 
-                    extract_version = max(45, zinfo.extract_version)
-                    create_version = max(45, zinfo.create_version)
-                else:
-                    extract_version = zinfo.extract_version
-                    create_version = zinfo.create_version
+                        extract_version = max(45, zinfo.extract_version)
+                        create_version = max(45, zinfo.create_version)
+                    else:
+                        extract_version = zinfo.extract_version
+                        create_version = zinfo.create_version
 
-                try:
-                    filename, flag_bits = zinfo._encodeFilenameFlags()
-                    centdir = struct.pack(structCentralDir,
-                     stringCentralDir, create_version,
-                     zinfo.create_system, extract_version, zinfo.reserved,
-                     flag_bits, zinfo.compress_type, dostime, dosdate,
-                     zinfo.CRC, compress_size, file_size,
-                     len(filename), len(extra_data), len(zinfo.comment),
-                     0, zinfo.internal_attr, zinfo.external_attr,
-                     header_offset)
-                except DeprecationWarning:
-                    print >>sys.stderr, (structCentralDir,
-                     stringCentralDir, create_version,
-                     zinfo.create_system, extract_version, zinfo.reserved,
-                     zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
-                     zinfo.CRC, compress_size, file_size,
-                     len(zinfo.filename), len(extra_data), len(zinfo.comment),
-                     0, zinfo.internal_attr, zinfo.external_attr,
-                     header_offset)
-                    raise
-                self.fp.write(centdir)
-                self.fp.write(filename)
-                self.fp.write(extra_data)
-                self.fp.write(zinfo.comment)
+                    try:
+                        filename, flag_bits = zinfo._encodeFilenameFlags()
+                        centdir = struct.pack(structCentralDir,
+                        stringCentralDir, create_version,
+                        zinfo.create_system, extract_version, zinfo.reserved,
+                        flag_bits, zinfo.compress_type, dostime, dosdate,
+                        zinfo.CRC, compress_size, file_size,
+                        len(filename), len(extra_data), len(zinfo.comment),
+                        0, zinfo.internal_attr, zinfo.external_attr,
+                        header_offset)
+                    except DeprecationWarning:
+                        print >>sys.stderr, (structCentralDir,
+                        stringCentralDir, create_version,
+                        zinfo.create_system, extract_version, zinfo.reserved,
+                        zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
+                        zinfo.CRC, compress_size, file_size,
+                        len(zinfo.filename), len(extra_data), len(zinfo.comment),
+                        0, zinfo.internal_attr, zinfo.external_attr,
+                        header_offset)
+                        raise
+                    self.fp.write(centdir)
+                    self.fp.write(filename)
+                    self.fp.write(extra_data)
+                    self.fp.write(zinfo.comment)
 
-            pos2 = self.fp.tell()
-            # Write end-of-zip-archive record
-            centDirCount = count
-            centDirSize = pos2 - pos1
-            centDirOffset = pos1
-            if (centDirCount >= ZIP_FILECOUNT_LIMIT or
-                centDirOffset > ZIP64_LIMIT or
-                centDirSize > ZIP64_LIMIT):
-                # Need to write the ZIP64 end-of-archive records
-                zip64endrec = struct.pack(
-                        structEndArchive64, stringEndArchive64,
-                        44, 45, 45, 0, 0, centDirCount, centDirCount,
-                        centDirSize, centDirOffset)
-                self.fp.write(zip64endrec)
+                pos2 = self.fp.tell()
+                # Write end-of-zip-archive record
+                centDirCount = count
+                centDirSize = pos2 - pos1
+                centDirOffset = pos1
+                if (centDirCount >= ZIP_FILECOUNT_LIMIT or
+                    centDirOffset > ZIP64_LIMIT or
+                    centDirSize > ZIP64_LIMIT):
+                    # Need to write the ZIP64 end-of-archive records
+                    zip64endrec = struct.pack(
+                            structEndArchive64, stringEndArchive64,
+                            44, 45, 45, 0, 0, centDirCount, centDirCount,
+                            centDirSize, centDirOffset)
+                    self.fp.write(zip64endrec)
 
-                zip64locrec = struct.pack(
-                        structEndArchive64Locator,
-                        stringEndArchive64Locator, 0, pos2, 1)
-                self.fp.write(zip64locrec)
-                centDirCount = min(centDirCount, 0xFFFF)
-                centDirSize = min(centDirSize, 0xFFFFFFFF)
-                centDirOffset = min(centDirOffset, 0xFFFFFFFF)
+                    zip64locrec = struct.pack(
+                            structEndArchive64Locator,
+                            stringEndArchive64Locator, 0, pos2, 1)
+                    self.fp.write(zip64locrec)
+                    centDirCount = min(centDirCount, 0xFFFF)
+                    centDirSize = min(centDirSize, 0xFFFFFFFF)
+                    centDirOffset = min(centDirOffset, 0xFFFFFFFF)
 
-            # check for valid comment length
-            if len(self.comment) >= ZIP_MAX_COMMENT:
-                if self.debug > 0:
-                    msg = 'Archive comment is too long; truncating to %d bytes' \
-                          % ZIP_MAX_COMMENT
-                self.comment = self.comment[:ZIP_MAX_COMMENT]
-
-            endrec = struct.pack(structEndArchive, stringEndArchive,
-                                 0, 0, centDirCount, centDirCount,
-                                 centDirSize, centDirOffset, len(self.comment))
-            self.fp.write(endrec)
-            self.fp.write(self.comment)
-            self.fp.flush()
-
-        if not self._filePassed:
-            self.fp.close()
-        self.fp = None
+                endrec = struct.pack(structEndArchive, stringEndArchive,
+                                    0, 0, centDirCount, centDirCount,
+                                    centDirSize, centDirOffset, len(self._comment))
+                self.fp.write(endrec)
+                self.fp.write(self._comment)
+                self.fp.flush()
+        finally:
+            fp = self.fp
+            self.fp = None
+            if not self._filePassed:
+                fp.close()
 
 
 class PyZipFile(ZipFile):
@@ -1381,16 +1466,15 @@
         if len(args) != 2:
             print USAGE
             sys.exit(1)
-        zf = ZipFile(args[1], 'r')
-        zf.printdir()
-        zf.close()
+        with ZipFile(args[1], 'r') as zf:
+            zf.printdir()
 
     elif args[0] == '-t':
         if len(args) != 2:
             print USAGE
             sys.exit(1)
-        zf = ZipFile(args[1], 'r')
-        badfile = zf.testzip()
+        with ZipFile(args[1], 'r') as zf:
+            badfile = zf.testzip()
         if badfile:
             print("The following enclosed file is corrupted: {!r}".format(badfile))
         print "Done testing"
@@ -1400,20 +1484,19 @@
             print USAGE
             sys.exit(1)
 
-        zf = ZipFile(args[1], 'r')
-        out = args[2]
-        for path in zf.namelist():
-            if path.startswith('./'):
-                tgt = os.path.join(out, path[2:])
-            else:
-                tgt = os.path.join(out, path)
+        with ZipFile(args[1], 'r') as zf:
+            out = args[2]
+            for path in zf.namelist():
+                if path.startswith('./'):
+                    tgt = os.path.join(out, path[2:])
+                else:
+                    tgt = os.path.join(out, path)
 
-            tgtdir = os.path.dirname(tgt)
-            if not os.path.exists(tgtdir):
-                os.makedirs(tgtdir)
-            with open(tgt, 'wb') as fp:
-                fp.write(zf.read(path))
-        zf.close()
+                tgtdir = os.path.dirname(tgt)
+                if not os.path.exists(tgtdir):
+                    os.makedirs(tgtdir)
+                with open(tgt, 'wb') as fp:
+                    fp.write(zf.read(path))
 
     elif args[0] == '-c':
         if len(args) < 3:
@@ -1429,11 +1512,9 @@
                             os.path.join(path, nm), os.path.join(zippath, nm))
             # else: ignore
 
-        zf = ZipFile(args[1], 'w', allowZip64=True)
-        for src in args[2:]:
-            addToZip(zf, src, os.path.basename(src))
-
-        zf.close()
+        with ZipFile(args[1], 'w', allowZip64=True) as zf:
+            for src in args[2:]:
+                addToZip(zf, src, os.path.basename(src))
 
 if __name__ == "__main__":
     main()
diff --git a/maven/build.xml b/maven/build.xml
--- a/maven/build.xml
+++ b/maven/build.xml
@@ -67,9 +67,13 @@
     <m2-bundle artifactId="jython-standalone"/>
   </target>
 
+  <target name="bundle-installer" depends="prep">
+    <m2-bundle artifactId="jython-installer"/>
+  </target>
+
   <target name="install-all" depends="install, install-standalone"/>
 
-  <target name="bundle-all" depends="bundle, bundle-standalone"/>
+  <target name="bundle-all" depends="bundle, bundle-standalone, bundle-installer"/>
 
   <target name="clean">
     <delete dir="${build.dir}"/>
diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java
--- a/src/org/python/core/BaseBytes.java
+++ b/src/org/python/core/BaseBytes.java
@@ -9,12 +9,12 @@
 import java.util.ListIterator;
 
 /**
- * Base class for Jython bytearray (and bytes in due course) that provides most of the Java API,
- * including Java List behaviour. Attempts to modify the contents through this API will throw a
- * TypeError if the actual type of the object is not mutable.
- * <p>
- * It is possible for a Java client to treat this class as a <tt>List<PyInteger></tt>, obtaining
- * equivalent functionality to the Python interface in a Java paradigm.
+ * Base class for Jython <code>bytearray</code> (and <code>bytes</code> in due course) that provides
+ * most of the Java API, including Java {@link List} behaviour. Attempts to modify the contents
+ * through this API will throw a <code>TypeError</code> if the actual type of the object is not
+ * mutable. It is possible for a Java client to treat this class as a
+ * <code>List<PyInteger></code>, obtaining equivalent functionality to the Python interface in a
+ * Java paradigm.
  * <p>
  * Subclasses must define (from {@link PySequence}):
  * <ul>
@@ -29,11 +29,29 @@
  * <li>{@link #delRange(int, int)}</li>
  * </ul>
  * since the default implementations will otherwise throw an exception.
+ * <p>
+ * Many of the methods implemented here are inherited or thinly wrapped by {@link PyByteArray},
+ * which offers them as Java API, or exposes them as Python methods. These prototype Python methods
+ * mostly accept a {@link PyObject} as argument, where you might have expected a <code>byte[]</code>
+ * or <code>BaseBytes</code>, in order to accommodate the full range of types accepted by the Python
+ * equivalent: usually, any <code>PyObject</code> that implements {@link BufferProtocol}, providing
+ * a one-dimensional array of bytes, is an acceptable argument. In the documentation, the reader
+ * will often see the terms "byte array" or "object viewable as bytes" instead of
+ * <code>BaseBytes</code> when this broader scope is intended.
+ * <p>
+ * Where the methods return a <code>BaseBytes</code>, this is will normally be an instance of the
+ * class of the object on which the method was actually called. For example {@link #capitalize()},
+ * defined in <code>BaseBytes</code> to return a BaseBytes, actually returns a {@link PyByteArray}
+ * when applied to a <code>bytearray</code>. Or it may be that the method returns a
+ * <code>PyList</code> of instances of the target type, for example {@link #rpartition(PyObject)}.
+ * This is achieved by the sub-class defining {@link #getslice(int, int, int)} and
+ * {@link #getBuilder(int)} to return instances of its own type. See the documentation of particular
+ * methods for more information.
  */
 public abstract class BaseBytes extends PySequence implements List<PyInteger> {
 
     /**
-     * Simple constructor of empty zero-length array of defined type.
+     * Constructs a zero-length <code>BaseBytes</code> of explicitly-specified sub-type.
      *
      * @param type explicit Jython type
      */
@@ -44,7 +62,7 @@
     }
 
     /**
-     * Simple constructor of zero-filled array of defined size and type.
+     * Constructs a zero-filled array of defined size and type.
      *
      * @param size required
      * @param type explicit Jython type
@@ -56,7 +74,7 @@
     }
 
     /**
-     * Construct byte array of defined type by copying values from int[].
+     * Constructs a byte array of defined type by copying values from int[].
      *
      * @param type explicit Jython type
      * @param value source of values (and size)
@@ -72,8 +90,8 @@
     }
 
     /**
-     * Construct byte array of defined type by copying character values from a String. These values
-     * have to be in the Python byte range 0 to 255.
+     * Constructs a byte array of defined type by copying character values from a String. These
+     * values have to be in the Python byte range 0 to 255.
      *
      * @param type explicit Jython type
      * @param value source of characters
@@ -250,7 +268,8 @@
      * @param encoding name of optional encoding
      * @param errors name of optional errors policy
      * @return encoded string
-     * @throws PyException(TypeError) if the PyString is actually a PyUnicode and encoding is null
+     * @throws PyException (TypeError) if the <code>PyString</code> is actually a {@link PyUnicode}
+     *             and encoding is <code>null</code>
      */
     protected static String encode(PyString arg, String encoding, String errors) throws PyException {
         // Jython encode emits a String (not byte[])
@@ -278,7 +297,7 @@
      *
      * @param start index in this byte array at which the first character code lands
      * @param value source of characters
-     * @throws PyException(ValueError) if any value[i] > 255
+     * @throws PyException (ValueError) if any value[i] > 255
      */
     protected void setBytes(int start, String value) throws PyException {
         int n = value.length();
@@ -294,7 +313,7 @@
      *
      * @param start index in this byte array at which the first character code lands
      * @param value source of characters
-     * @throws PyException(ValueError) if any value[i] > 255
+     * @throws PyException (ValueError) if any value[i] > 255
      */
     protected void setBytes(int start, int step, String value) throws PyException {
         int n = value.length();
@@ -307,7 +326,7 @@
 
     /**
      * Helper for <code>__new__</code> and <code>__init__</code> and the Java API constructor from
-     * int in subclasses. Construct zero-filled bytearray of specified size.
+     * int in subclasses. Construct zero-filled byte array of specified size.
      *
      * @param n size of zero-filled array
      */
@@ -332,24 +351,11 @@
         view.copyTo(storage, offset);
     }
 
-// /**
-// * Helper for the Java API constructor from a {@link #PyBuffer}. View is (perhaps) a stop-gap
-// until
-// * the Jython implementation of PEP 3118 (buffer API) is embedded.
-// *
-// * @param value a byte-oriented view
-// */
-// void init(PyBuffer value) {
-// int n = value.getLen();
-// newStorage(n);
-// value.copyTo(storage, offset);
-// }
-//
     /**
      * Helper for <code>__new__</code> and <code>__init__</code> and the Java API constructor from
-     * bytearray or bytes in subclasses.
+     * <code>bytearray</code> or <code>bytes</code> in subclasses.
      *
-     * @param source bytearray (or bytes) to copy
+     * @param source <code>bytearray</code> (or <code>bytes</code>) to copy
      */
     protected void init(BaseBytes source) {
         newStorage(source.size);
@@ -429,8 +435,8 @@
          * Load bytes into the container from the given iterable
          *
          * @param iter iterable source of values to enter into the container
-         * @throws PyException(TypeError) if any value not acceptable type
-         * @throws PyException(ValueError) if any value<0 or value>255 or string length!=1
+         * @throws PyException (TypeError) if any value not acceptable type
+         * @throws PyException (ValueError) if any value<0 or value>255 or string length!=1
          */
         void loadFrom(Iterable<? extends PyObject> iter) throws PyException {
 
@@ -535,7 +541,7 @@
      * Check that an index is within the range of the array, that is <tt>0<=index<size</tt>.
      *
      * @param index to check
-     * @throws PyException(IndexError) if the index is outside the array bounds
+     * @throws PyException (IndexError) if the index is outside the array bounds
      */
     protected final void indexCheck(int index) throws PyException {
         if (index < 0 || index >= size) {
@@ -569,7 +575,7 @@
      * 0..255.)
      *
      * @param value to convert.
-     * @throws PyException(ValueError) if value<0 or value>255
+     * @throws PyException (ValueError) if value<0 or value>255
      */
     protected static final byte byteCheck(int value) throws PyException {
         if (value < 0 || value > 255) {
@@ -584,7 +590,7 @@
      * Python bytes run 0..255.)
      *
      * @param value to convert.
-     * @throws PyException(ValueError) if value<0 or value>255
+     * @throws PyException (ValueError) if value<0 or value>255
      */
     protected static final byte byteCheck(PyInteger value) throws PyException {
         return byteCheck(value.asInt());
@@ -602,8 +608,8 @@
      * </ul>
      *
      * @param value to convert.
-     * @throws PyException(TypeError) if not acceptable type
-     * @throws PyException(ValueError) if value<0 or value>255 or string length!=1
+     * @throws PyException (TypeError) if not acceptable type
+     * @throws PyException (ValueError) if value<0 or value>255 or string length!=1
      */
     protected static final byte byteCheck(PyObject value) throws PyException {
         if (value.isIndex()) {
@@ -627,7 +633,7 @@
      * if the return value is not <code>null</code>.
      *
      * @param b object to wrap
-     * @return byte-oriented view or null
+     * @return byte-oriented view or <code>null</code>
      */
     protected static PyBuffer getView(PyObject b) {
 
@@ -673,6 +679,7 @@
      * API for org.python.core.PySequence
      * ============================================================================================
      */
+    @Override
     protected PyInteger pyget(int index) {
         return new PyInteger(intAt(index));
     }
@@ -681,8 +688,10 @@
      * We're not implementing these here, but we can give a stronger guarantee about the return type
      * and save some casting and type anxiety.
      */
+    @Override
     protected abstract BaseBytes getslice(int start, int stop, int step);
 
+    @Override
     protected abstract BaseBytes repeat(int count);
 
     /*
@@ -696,9 +705,9 @@
      *
      * @param index to insert at
      * @param element to insert (by value)
-     * @throws PyException(IndexError) if the index is outside the array bounds
-     * @throws PyException(ValueError) if element<0 or element>255
-     * @throws PyException(TypeError) if the subclass is immutable
+     * @throws PyException (IndexError) if the index is outside the array bounds
+     * @throws PyException (ValueError) if element<0 or element>255
+     * @throws PyException (TypeError) if the subclass is immutable
      */
     public void pyinsert(int index, PyObject element) {
         // This won't succeed: it just produces the right error.
@@ -720,8 +729,8 @@
     }
 
     /**
-     * Class defining the behaviour of bytearray with respect to slice assignment, etc., which
-     * differs from the default (list) behaviour in small ways.
+     * Class defining the behaviour of <code>bytearray</code> with respect to slice assignment,
+     * etc., which differs from the default (list) behaviour in small ways.
      */
     private class IndexDelegate extends PySequence.DefaultIndexDelegate {
 
@@ -886,11 +895,11 @@
     }
 
     /**
-     * Implementation of __eq__ (equality) operator, capable of comparison with another byte array
-     * or bytes. Comparison with an invalid type returns null.
+     * Implementation of __eq__ (equality) operator, capable of comparison with another byte array.
+     * Comparison with an invalid type returns <code>null</code>.
      *
      * @param other Python object to compare with
-     * @return Python boolean result or null if not implemented for the other type.
+     * @return Python boolean result or <code>null</code> if not implemented for the other type.
      */
     final PyObject basebytes___eq__(PyObject other) {
         int cmp = basebytes_cmpeq(other);
@@ -904,11 +913,11 @@
     }
 
     /**
-     * Implementation of __ne__ (not equals) operator, capable of comparison with another byte array
-     * or bytes. Comparison with an invalid type returns null.
+     * Implementation of __ne__ (not equals) operator, capable of comparison with another byte
+     * array. Comparison with an invalid type returns <code>null</code>.
      *
      * @param other Python object to compare with
-     * @return Python boolean result or null if not implemented for the other type.
+     * @return Python boolean result or <code>null</code> if not implemented for the other type.
      */
     final PyObject basebytes___ne__(PyObject other) {
         int cmp = basebytes_cmpeq(other);
@@ -922,11 +931,11 @@
     }
 
     /**
-     * Implementation of __lt__ (less than) operator, capable of comparison with another byte array
-     * or bytes. Comparison with an invalid type returns null.
+     * Implementation of __lt__ (less than) operator, capable of comparison with another byte array.
+     * Comparison with an invalid type returns <code>null</code>.
      *
      * @param other Python object to compare with
-     * @return Python boolean result or null if not implemented for the other type.
+     * @return Python boolean result or <code>null</code> if not implemented for the other type.
      */
     final PyObject basebytes___lt__(PyObject other) {
         int cmp = basebytes_cmp(other);
@@ -941,10 +950,10 @@
 
     /**
      * Implementation of __le__ (less than or equal to) operator, capable of comparison with another
-     * byte array or bytes. Comparison with an invalid type returns null.
+     * byte array. Comparison with an invalid type returns <code>null</code>.
      *
      * @param other Python object to compare with
-     * @return Python boolean result or null if not implemented for the other type.
+     * @return Python boolean result or <code>null</code> if not implemented for the other type.
      */
     final PyObject basebytes___le__(PyObject other) {
         int cmp = basebytes_cmp(other);
@@ -959,10 +968,10 @@
 
     /**
      * Implementation of __ge__ (greater than or equal to) operator, capable of comparison with
-     * another byte array or bytes. Comparison with an invalid type returns null.
+     * another byte array. Comparison with an invalid type returns <code>null</code>.
      *
      * @param other Python object to compare with
-     * @return Python boolean result or null if not implemented for the other type.
+     * @return Python boolean result or <code>null</code> if not implemented for the other type.
      */
     final PyObject basebytes___ge__(PyObject other) {
         int cmp = basebytes_cmp(other);
@@ -977,10 +986,10 @@
 
     /**
      * Implementation of __gt__ (greater than) operator, capable of comparison with another byte
-     * array or bytes. Comparison with an invalid type returns null.
+     * array. Comparison with an invalid type returns <code>null</code>.
      *
      * @param other Python object to compare with
-     * @return Python boolean result or null if not implemented for the other type.
+     * @return Python boolean result or <code>null</code> if not implemented for the other type.
      */
     final PyObject basebytes___gt__(PyObject other) {
         int cmp = basebytes_cmp(other);
@@ -1031,7 +1040,7 @@
      * @param ostart of slice to search.
      * @param oend of slice to search.
      * @param endswith true if we are doing endswith, false if startswith.
-     * @return true if and only if this bytearray ends with (one of) <code>target</code>.
+     * @return true if and only if this byte array ends with (one of) <code>target</code>.
      */
     protected final synchronized boolean basebytes_starts_or_endswith(PyObject target,
             PyObject ostart, PyObject oend, boolean endswith) {
@@ -1151,7 +1160,7 @@
      * error policy. The returned PyObject will usually be a <code>PyUnicode</code>, but in practice
      * it is whatever the <code>decode</code> method of the codec decides.
      *
-     * @param encoding the name of the codec (uses default codec if null)
+     * @param encoding the name of the codec (uses default codec if <code>null</code>)
      * @return object containing the decoded characters
      */
     public PyObject decode(String encoding) {
@@ -1163,8 +1172,8 @@
      * policy. The returned PyObject will usually be a <code>PyUnicode</code>, but in practice it is
      * whatever the <code>decode</code> method of the codec decides.
      *
-     * @param encoding the name of the codec (uses default codec if null)
-     * @param errors the name of the error policy (uses 'strict' if null)
+     * @param encoding the name of the codec (uses default codec if <code>null</code>)
+     * @param errors the name of the error policy (uses 'strict' if <code>null</code>)
      * @return object containing the decoded characters
      */
     public PyObject decode(String encoding, String errors) {
@@ -1210,6 +1219,7 @@
      *
      * @return PyTuple that is first stage in pickling byte array
      */
+    @Override
     public PyObject __reduce__() {
         return basebytes___reduce__();
     }
@@ -1555,6 +1565,7 @@
          * the table is only 32 elements long, rather than (as it might be) 256 bytes, the number of
          * distinct byte values.
          */
+        @Override
         protected int[] calculateSkipTable() {
             int[] skipTable = new int[MASK + 1];
             int m = pattern.getLen();
@@ -1572,6 +1583,7 @@
          *
          * @return the new effective end of the text
          */
+        @Override
         public int currIndex() {
             return right + pattern.getLen() - 1;
         }
@@ -1583,6 +1595,7 @@
          *
          * @return matching index or -1 if no (further) occurrences found
          */
+        @Override
         public int nextIndex() {
 
             int m = pattern.getLen();
@@ -1855,7 +1868,7 @@
      *
      * @param result to receive the decoded values
      * @param hex specification of the bytes
-     * @throws PyException(ValueError) if non-hex characters, or isolated ones, are encountered
+     * @throws PyException (ValueError) if non-hex characters, or isolated ones, are encountered
      */
     static void basebytes_fromhex(BaseBytes result, String hex) throws PyException {
 
@@ -1937,7 +1950,7 @@
                     // Unsuitable object to be in this join
                     String fmt = "can only join an iterable of bytes (item %d has type '%.80s')";
                     throw Py.TypeError(String.format(fmt, iterList.size(), o.getType()
-                                                                            .fastGetName()));
+                            .fastGetName()));
                 }
                 iterList.add(v);
                 totalSize += v.getLen();
@@ -1990,6 +2003,9 @@
      * the part before the separator, the separator itself, and the part after the separator. If the
      * separator is not found, return a 3-tuple containing the string itself, followed by two empty
      * byte arrays.
+     * <p>
+     * The elements of the <code>PyTuple</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param sep the separator on which to partition this byte array
      * @return a tuple of (head, separator, tail)
@@ -2000,6 +2016,9 @@
 
     /**
      * Ready-to-expose implementation of Python <code>partition(sep)</code>.
+     * <p>
+     * The elements of the <code>PyTuple</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param sep the separator on which to partition this byte array
      * @return a tuple of (head, separator, tail)
@@ -2032,6 +2051,9 @@
     /**
      * Construct return value for implementation of Python <code>partition(sep)</code> or
      * <code>rpartition(sep)</code>, returns [0:p], [p:q], [q:]
+     * <p>
+     * The elements of the <code>PyTuple</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param p start of separator
      * @param q start of tail
@@ -2430,6 +2452,9 @@
      * containing the part before the separator, the separator itself, and the part after the
      * separator. If the separator is not found, return a 3-tuple containing two empty byte arrays,
      * followed by the byte array itself.
+     * <p>
+     * The elements of the <code>PyTuple</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param sep the separator on which to partition this byte array
      * @return a tuple of (head, separator, tail)
@@ -2440,6 +2465,9 @@
 
     /**
      * Ready-to-expose implementation of Python <code>rpartition(sep)</code>.
+     * <p>
+     * The elements of the <code>PyTuple</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param sep the separator on which to partition this byte array
      * @return a tuple of (head, separator, tail)
@@ -2471,6 +2499,9 @@
     /**
      * Implementation of Python <code>rsplit()</code>, that returns a list of the words in the byte
      * array, using whitespace as the delimiter. See {@link #rsplit(PyObject, int)}.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @return PyList of byte arrays that result from the split
      */
@@ -2480,10 +2511,13 @@
 
     /**
      * Implementation of Python <code>rsplit(sep)</code>, that returns a list of the words in the
-     * byte array, using sep as the delimiter. See {@link #rsplit(PyObject, int)} for the semantics
-     * of the separator.
+     * byte array, using <code>sep</code> as the delimiter. See {@link #rsplit(PyObject, int)} for
+     * the semantics of the separator.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @return PyList of byte arrays that result from the split
      */
     public PyList rsplit(PyObject sep) {
@@ -2492,22 +2526,26 @@
 
     /**
      * Implementation of Python <code>rsplit(sep, maxsplit)</code>, that returns a list of the words
-     * in the byte array, using sep as the delimiter. If maxsplit is given, at most maxsplit splits
-     * are done (thus, the list will have at most maxsplit+1 elements). If maxsplit is not
-     * specified, then there is no limit on the number of splits (all possible splits are made).
+     * in the byte array, using <code>sep</code> as the delimiter. If <code>maxsplit</code> is
+     * given, at most <code>maxsplit</code> splits are done (thus, the list will have at most
+     * <code>maxsplit+1</code> elements). If <code>maxsplit</code> is not specified, then there is
+     * no limit on the number of splits (all possible splits are made).
      * <p>
-     * The semantics of sep and maxcount are identical to those of <code>split(sep, maxsplit)</code>
-     * , except that splits are generated from the right (and pushed onto the front of the result
-     * list). The result is only different from that of <code>split</code> if <code>maxcount</code>
-     * limits the number of splits. For example,
+     * The semantics of <code>sep</code> and maxcount are identical to those of
+     * <code>split(sep, maxsplit)</code> , except that splits are generated from the right (and
+     * pushed onto the front of the result list). The result is only different from that of
+     * <code>split</code> if <code>maxcount</code> limits the number of splits. For example,
      * <ul>
      * <li><code>bytearray(b' 1  2   3  ').rsplit()</code> returns
      * <code>[bytearray(b'1'), bytearray(b'2'), bytearray(b'3')]</code>, and</li>
      * <li><code>bytearray(b'  1  2   3  ').rsplit(None, 1)</code> returns
      * <code>[bytearray(b'  1 2'), bytearray(b'3')]</code></li>.
      * </ul>
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
      */
@@ -2517,10 +2555,13 @@
 
     /**
      * Ready-to-expose implementation of Python <code>rsplit(sep, maxsplit)</code>, that returns a
-     * list of the words in the byte array, using sep as the delimiter. Use the defines whitespace
-     * semantics if sep is null.
+     * list of the words in the byte array, using <code>sep</code> as the delimiter. Use the defines
+     * whitespace semantics if <code>sep</code> is <code>null</code>.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
      */
@@ -2534,11 +2575,15 @@
 
     /**
      * Implementation of Python <code>rsplit(sep, maxsplit)</code>, that returns a list of the words
-     * in the byte array, using sep (which is not null) as the delimiter. If maxsplit>=0, at most
-     * maxsplit splits are done (thus, the list will have at most maxsplit+1 elements). If
-     * maxsplit<0, then there is no limit on the number of splits (all possible splits are made).
+     * in the byte array, using <code>sep</code> (which is not <code>null</code>) as the delimiter.
+     * If <code>maxsplit>=0</code>, at most <code>maxsplit</code> splits are done (thus, the list
+     * will have at most <code>maxsplit+1</code> elements). If <code>maxsplit<0</code>, then
+     * there is no limit on the number of splits (all possible splits are made).
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
      */
@@ -2589,15 +2634,18 @@
 
     /**
      * Implementation of Python <code>rsplit(None, maxsplit)</code>, that returns a list of the
-     * words in the byte array, using whitespace as the delimiter. If maxsplit is given, at most
-     * maxsplit splits are done (thus, the list will have at most maxsplit+1 elements). If maxsplit
-     * is not specified, then there is no limit on the number of splits (all possible splits are
-     * made).
+     * words in the byte array, using whitespace as the delimiter. If <code>maxsplit</code> is
+     * given, at most <code>maxsplit</code> splits are done (thus, the list will have at most
+     * <code>maxsplit+1</code> elements). If maxsplit is not specified, then there is no limit on
+     * the number of splits (all possible splits are made).
      * <p>
      * Runs of consecutive whitespace are regarded as a single separator, and the result will
      * contain no empty strings at the start or end if the string has leading or trailing
      * whitespace. Consequently, splitting an empty string or a string consisting of just whitespace
-     * with a None separator returns [].
+     * with a <code>None</code> separator returns [].
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this/self</code>.
      *
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
@@ -2648,6 +2696,9 @@
     /**
      * Implementation of Python <code>split()</code>, that returns a list of the words in the byte
      * array, using whitespace as the delimiter. See {@link #split(PyObject, int)}.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @return PyList of byte arrays that result from the split
      */
@@ -2657,10 +2708,13 @@
 
     /**
      * Implementation of Python <code>split(sep)</code>, that returns a list of the words in the
-     * byte array, using sep as the delimiter. See {@link #split(PyObject, int)} for the semantics
-     * of the separator.
+     * byte array, using <code>sep</code> as the delimiter. See {@link #split(PyObject, int)} for
+     * the semantics of the separator.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @return PyList of byte arrays that result from the split
      */
     public PyList split(PyObject sep) {
@@ -2669,29 +2723,33 @@
 
     /**
      * Implementation of Python <code>split(sep, maxsplit)</code>, that returns a list of the words
-     * in the byte array, using sep as the delimiter. If maxsplit is given, at most maxsplit splits
-     * are done (thus, the list will have at most maxsplit+1 elements). If maxsplit is not
-     * specified, then there is no limit on the number of splits (all possible splits are made).
+     * in the byte array, using <code>sep</code> as the delimiter. If <code>maxsplit</code> is
+     * given, at most <code>maxsplit</code> splits are done. (Thus, the list will have at most
+     * <code>maxsplit+1</code> elements). If <code>maxsplit</code> is not specified, then there is
+     * no limit on the number of splits (all possible splits are made).
      * <p>
-     * If sep is given, consecutive delimiters are not grouped together and are deemed to delimit
-     * empty strings (for example, '1,,2'.split(',') returns ['1', '', '2']). The sep argument may
-     * consist of multiple characters (for example, '1<>2<>3'.split('<>') returns ['1',
-     * '2', '3']). Splitting an empty string with a specified separator returns [''].
+     * If <code>sep</code> is given, consecutive delimiters are not grouped together and are deemed
+     * to delimit empty strings (for example, <code>'1,,2'.split(',')</code> returns
+     * <code>['1', '', '2']</code>). The <code>sep</code> argument may consist of multiple
+     * characters (for example, <code>'1<>2<>3'.split('<>')</code> returns <code>['1',
+     * '2', '3']</code>). Splitting an empty string with a specified separator <code> ['']</code>.
      * <p>
-     * If sep is not specified or is None, a different splitting algorithm is applied: runs of
-     * consecutive whitespace are regarded as a single separator, and the result will contain no
-     * empty strings at the start or end if the string has leading or trailing whitespace.
-     * Consequently, splitting an empty string or a string consisting of just whitespace with a None
-     * separator returns []. For example,
-     *
+     * If <code>sep</code> is not specified or is <code>None</code>, a different splitting algorithm
+     * is applied: runs of consecutive whitespace are regarded as a single separator, and the result
+     * will contain no empty strings at the start or end if the string has leading or trailing
+     * whitespace. Consequently, splitting an empty string or a string consisting of just whitespace
+     * with a <code>None</code> separator returns <code>[]</code>. For example,
      * <ul>
      * <li><code>bytearray(b' 1  2   3  ').split()</code> returns
      * <code>[bytearray(b'1'), bytearray(b'2'), bytearray(b'3')]</code>, and</li>
      * <li><code>bytearray(b'  1  2   3  ').split(None, 1)</code> returns
      * <code>[bytearray(b'1'), bytearray(b'2   3  ')]</code>.</li>
      * </ul>
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
      */
@@ -2701,10 +2759,13 @@
 
     /**
      * Ready-to-expose implementation of Python <code>split(sep, maxsplit)</code>, that returns a
-     * list of the words in the byte array, using sep as the delimiter. Use the defines whitespace
-     * semantics if sep is null.
+     * list of the words in the byte array, using <code>sep</code> as the delimiter. Use the defines
+     * whitespace semantics if <code>sep</code> is <code>null</code>.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
      */
@@ -2718,11 +2779,15 @@
 
     /**
      * Implementation of Python <code>split(sep, maxsplit)</code>, that returns a list of the words
-     * in the byte array, using sep (which is not null) as the delimiter. If maxsplit>=0, at most
-     * maxsplit splits are done (thus, the list will have at most maxsplit+1 elements). If
-     * maxsplit<0, then there is no limit on the number of splits (all possible splits are made).
+     * in the byte array, using <code>sep</code> (which is not <code>null</code>) as the delimiter.
+     * If <code>maxsplit>=0</code>, at most <code>maxsplit</code> splits are done (thus, the list
+     * will have at most <code>maxsplit+1</code> elements). If <code>maxsplit<0</code>, then
+     * there is no limit on the number of splits (all possible splits are made).
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
-     * @param sep bytes, or object viewable as bytes, defining the separator
+     * @param sep <code>bytes</code>, or object viewable as bytes, defining the separator
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
      */
@@ -2763,14 +2828,18 @@
 
     /**
      * Implementation of Python <code>split(None, maxsplit)</code>, that returns a list of the words
-     * in the byte array, using whitespace as the delimiter. If maxsplit is given, at most maxsplit
-     * splits are done (thus, the list will have at most maxsplit+1 elements). If maxsplit is not
-     * specified, then there is no limit on the number of splits (all possible splits are made).
+     * in the byte array, using whitespace as the delimiter. If <code>maxsplit</code> is given, at
+     * most maxsplit splits are done (thus, the list will have at most <code>maxsplit+1</code>
+     * elements). If <code>maxsplit</code> is not specified, then there is no limit on the number of
+     * splits (all possible splits are made).
      * <p>
      * Runs of consecutive whitespace are regarded as a single separator, and the result will
      * contain no empty strings at the start or end if the string has leading or trailing
      * whitespace. Consequently, splitting an empty string or a string consisting of just whitespace
-     * with a None separator returns [].
+     * with a <code>None</code> separator returns [].
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param maxsplit maximum number of splits
      * @return PyList of byte arrays that result from the split
@@ -2815,6 +2884,9 @@
     /**
      * Implementation of Python <code>splitlines()</code>, returning a list of the lines in the byte
      * array, breaking at line boundaries. Line breaks are not included in the resulting segments.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @return List of segments
      */
@@ -2826,6 +2898,9 @@
      * Implementation of Python <code>splitlines(keepends)</code>, returning a list of the lines in
      * the string, breaking at line boundaries. Line breaks are not included in the resulting list
      * unless <code>keepends</code> is true.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param keepends if true, include the end of line bytes(s)
      * @return PyList of segments
@@ -2838,6 +2913,9 @@
      * Ready-to-expose implementation of Python <code>splitlines(keepends)</code>, returning a list
      * of the lines in the array, breaking at line boundaries. Line breaks are not included in the
      * resulting list unless keepends is given and true.
+     * <p>
+     * The elements of the <code>PyList</code> returned by this method are instances of the same
+     * actual type as <code>this</code>.
      *
      * @param keepends if true, include the end of line bytes(s)
      * @return List of segments
@@ -2888,7 +2966,7 @@
      * byte.
      *
      * @param function name
-     * @param fillchar or null
+     * @param fillchar or <code>null</code>
      * @return
      */
     protected static byte fillByteCheck(String function, String fillchar) {
@@ -2937,7 +3015,7 @@
      * exactly the original object.)
      *
      * @param width desired
-     * @param fillchar one-byte String to fill with, or null implying space
+     * @param fillchar one-byte String to fill with, or <code>null</code> implying space
      * @return (possibly new) byte array containing the result
      */
     final BaseBytes basebytes_center(int width, String fillchar) {
@@ -2958,7 +3036,7 @@
      * exactly the original object.)
      *
      * @param width desired
-     * @param fillchar one-byte String to fill with, or null implying space
+     * @param fillchar one-byte String to fill with, or <code>null</code> implying space
      * @return (possibly new) byte array containing the result
      */
     final BaseBytes basebytes_ljust(int width, String fillchar) {
@@ -2977,7 +3055,7 @@
      * exactly the original object.)
      *
      * @param width desired
-     * @param fillchar one-byte String to fill with, or null implying space
+     * @param fillchar one-byte String to fill with, or <code>null</code> implying space
      * @return (possibly new) byte array containing the result
      */
     final BaseBytes basebytes_rjust(int width, String fillchar) {
@@ -3323,7 +3401,8 @@
     /**
      * Java API equivalent of Python <code>capitalize()</code>. This method treats the bytes as
      * Unicode pont codes and is consistent with Java's {@link Character#toUpperCase(char)} and
-     * {@link Character#toLowerCase(char)}.
+     * {@link Character#toLowerCase(char)}. The <code>BaseBytes</code> returned by this method has
+     * the same actual type as <code>this/self</code>.
      *
      * @return a copy of the array with its first character capitalized and the rest lowercased.
      */
@@ -3332,7 +3411,9 @@
     }
 
     /**
-     * Ready-to-expose implementation of Python <code>capitalize()</code>.
+     * Ready-to-expose implementation of Python <code>capitalize()</code>. The
+     * <code>BaseBytes</code> returned by this method has the same actual type as
+     * <code>this/self</code>.
      *
      * @return a copy of the array with its first character capitalized and the rest lowercased.
      */
@@ -3365,7 +3446,9 @@
 
     /**
      * Java API equivalent of Python <code>lower()</code>. This method treats the bytes as Unicode
-     * pont codes and is consistent with Java's {@link Character#toLowerCase(char)}.
+     * pont codes and is consistent with Java's {@link Character#toLowerCase(char)}. The
+     * <code>BaseBytes</code> returned by this method has the same actual type as
+     * <code>this/self</code>.
      *
      * @return a copy of the array with all the cased characters converted to lowercase.
      */
@@ -3374,7 +3457,8 @@
     }
 
     /**
-     * Ready-to-expose implementation of Python <code>lower()</code>.
+     * Ready-to-expose implementation of Python <code>lower()</code>. The <code>BaseBytes</code>
+     * returned by this method has the same actual type as <code>this/self</code>.
      *
      * @return a copy of the array with all the cased characters converted to lowercase.
      */
@@ -3397,7 +3481,8 @@
     /**
      * Java API equivalent of Python <code>swapcase()</code>. This method treats the bytes as
      * Unicode pont codes and is consistent with Java's {@link Character#toUpperCase(char)} and
-     * {@link Character#toLowerCase(char)}.
+     * {@link Character#toLowerCase(char)}. The <code>BaseBytes</code> returned by this method has
+     * the same actual type as <code>this/self</code>.
      *
      * @return a copy of the array with uppercase characters converted to lowercase and vice versa.
      */
@@ -3406,7 +3491,8 @@
     }
 
     /**
-     * Ready-to-expose implementation of Python <code>swapcase()</code>.
+     * Ready-to-expose implementation of Python <code>swapcase()</code>. The <code>BaseBytes</code>
+     * returned by this method has the same actual type as <code>this/self</code>.
      *
      * @return a copy of the array with uppercase characters converted to lowercase and vice versa.
      */
@@ -3432,7 +3518,8 @@
      * Java API equivalent of Python <code>title()</code>. The algorithm uses a simple
      * language-independent definition of a word as groups of consecutive letters. The definition
      * works in many contexts but it means that apostrophes in contractions and possessives form
-     * word boundaries, which may not be the desired result.
+     * word boundaries, which may not be the desired result. The <code>BaseBytes</code> returned by
+     * this method has the same actual type as <code>this/self</code>.
      *
      * @return a titlecased version of the array where words start with an uppercase character and
      *         the remaining characters are lowercase.
@@ -3442,7 +3529,8 @@
     }
 
     /**
-     * Ready-to-expose implementation of Python <code>title()</code>.
+     * Ready-to-expose implementation of Python <code>title()</code>. The <code>BaseBytes</code>
+     * returned by this method has the same actual type as <code>this/self</code>.
      *
      * @return a titlecased version of the array where words start with an uppercase character and
      *         the remaining characters are lowercase.
@@ -3481,7 +3569,8 @@
     /**
      * Java API equivalent of Python <code>upper()</code>. Note that
      * <code>x.upper().isupper()</code> might be <code>false</code> if the array contains uncased
-     * characters.
+     * characters. The <code>BaseBytes</code> returned by this method has the same actual type as
+     * <code>this/self</code>.
      *
      * @return a copy of the array with all the cased characters converted to uppercase.
      */
@@ -3490,7 +3579,8 @@
     }
 
     /**
-     * Ready-to-expose implementation of Python <code>upper()</code>.
+     * Ready-to-expose implementation of Python <code>upper()</code>. The <code>BaseBytes</code>
+     * returned by this method has the same actual type as <code>this/self</code>.
      *
      * @return a copy of the array with all the cased characters converted to uppercase.
      */
@@ -3533,7 +3623,7 @@
      *
      * @param index of value in byte array
      * @return the integer value at the index
-     * @throws PyException(IndexError) if the index is outside the array bounds
+     * @throws PyException (IndexError) if the index is outside the array bounds
      */
     public synchronized int intAt(int index) throws PyException {
         indexCheck(index);
@@ -3599,7 +3689,7 @@
      */
     private static final void appendHexEscape(StringBuilder buf, int c) {
         buf.append("\\x").append(Character.forDigit((c & 0xf0) >> 4, 16))
-           .append(Character.forDigit(c & 0xf, 16));
+                .append(Character.forDigit(c & 0xf, 16));
     }
 
     /**
@@ -3666,8 +3756,8 @@
      */
 
     /**
-     * Access to the bytearray (or bytes) as a {@link java.util.List}. The List interface supplied
-     * by BaseBytes delegates to this object.
+     * Access to the byte array as a {@link java.util.List}. The List interface supplied by
+     * BaseBytes delegates to this object.
      */
     protected final List<PyInteger> listDelegate = new AbstractList<PyInteger>() {
 
@@ -3689,9 +3779,9 @@
          * Replaces the element at the specified position in this list with the specified element.
          *
          * @see java.util.AbstractList#set(int, java.lang.Object)
-         * @throws PyException(TypeError) if actual class is immutable
-         * @throws PyException(IndexError) if the index is outside the array bounds
-         * @throws PyException(ValueError) if element<0 or element>255
+         * @throws PyException (TypeError) if actual class is immutable
+         * @throws PyException (IndexError) if the index is outside the array bounds
+         * @throws PyException (ValueError) if element<0 or element>255
          */
         @Override
         public PyInteger set(int index, PyInteger element) throws PyException {
@@ -3707,9 +3797,9 @@
          * currently at that position and any subsequent elements to the right.
          *
          * @see java.util.AbstractList#add(int, java.lang.Object)
-         * @throws PyException(IndexError) if the index is outside the array bounds
-         * @throws PyException(ValueError) if element<0 or element>255
-         * @throws PyException(TypeError) if the owning concrete subclass is immutable
+         * @throws PyException (IndexError) if the index is outside the array bounds
+         * @throws PyException (ValueError) if element<0 or element>255
+         * @throws PyException (TypeError) if the owning concrete subclass is immutable
          */
         @Override
         public void add(int index, PyInteger element) throws PyException {
@@ -3724,7 +3814,7 @@
          * removed from the list.
          *
          * @see java.util.AbstractList#remove(int)
-         * @throws PyException(IndexError) if the index is outside the array bounds
+         * @throws PyException (IndexError) if the index is outside the array bounds
          */
         @Override
         public PyInteger remove(int index) {
@@ -3737,11 +3827,12 @@
     };
 
     /**
-     * Number of bytes in bytearray (or bytes) object.
+     * Number of bytes in <code>bytearray</code> (or <code>bytes</code>) object.
      *
      * @see java.util.List#size()
      * @return Number of bytes in byte array.
      * */
+    @Override
     public int size() {
         return size;
     }
@@ -3749,6 +3840,7 @@
     /*
      * @see java.util.List#isEmpty()
      */
+    @Override
     public boolean isEmpty() {
         return size == 0;
     }
@@ -3757,6 +3849,7 @@
      * Returns true if this list contains the specified value. More formally, returns true if and
      * only if this list contains at least one integer e such that o.equals(PyInteger(e)).
      */
+    @Override
     public boolean contains(Object o) {
         return listDelegate.contains(o);
     }
@@ -3764,6 +3857,7 @@
     /*
      * @see java.util.List#iterator()
      */
+    @Override
     public Iterator<PyInteger> iterator() {
         return listDelegate.iterator();
     }
@@ -3771,6 +3865,7 @@
     /*
      * @see java.util.List#toArray()
      */
+    @Override
     public Object[] toArray() {
         return listDelegate.toArray();
     }
@@ -3778,6 +3873,7 @@
     /*
      * @see java.util.List#toArray(T[])
      */
+    @Override
     public <T> T[] toArray(T[] a) {
         return listDelegate.toArray(a);
     }
@@ -3785,6 +3881,7 @@
     /*
      * @see java.util.List#add(java.lang.Object)
      */
+    @Override
     public boolean add(PyInteger o) {
         return listDelegate.add(o);
     }
@@ -3792,6 +3889,7 @@
     /*
      * @see java.util.List#remove(java.lang.Object)
      */
+    @Override
     public boolean remove(Object o) {
         return listDelegate.remove(o);
     }
@@ -3799,6 +3897,7 @@
     /*
      * @see java.util.List#containsAll(java.util.Collection)
      */
+    @Override
     public boolean containsAll(Collection<?> c) {
         return listDelegate.containsAll(c);
     }
@@ -3806,6 +3905,7 @@
     /*
      * @see java.util.List#addAll(java.util.Collection)
      */
+    @Override
     public boolean addAll(Collection<? extends PyInteger> c) {
         return listDelegate.addAll(c);
     }
@@ -3813,6 +3913,7 @@
     /*
      * @see java.util.List#addAll(int, java.util.Collection)
      */
+    @Override
     public boolean addAll(int index, Collection<? extends PyInteger> c) {
         return listDelegate.addAll(index, c);
     }
@@ -3820,6 +3921,7 @@
     /*
      * @see java.util.List#removeAll(java.util.Collection)
      */
+    @Override
     public boolean removeAll(Collection<?> c) {
         return listDelegate.removeAll(c);
     }
@@ -3827,6 +3929,7 @@
     /*
      * @see java.util.List#retainAll(java.util.Collection)
      */
+    @Override
     public boolean retainAll(Collection<?> c) {
         return listDelegate.retainAll(c);
     }
@@ -3834,6 +3937,7 @@
     /*
      * @see java.util.List#clear()
      */
+    @Override
     public void clear() {
         listDelegate.clear();
     }
@@ -3865,6 +3969,7 @@
     /*
      * @see java.util.List#hashCode()
      */
+    @Override
     public int hashCode() {
         return listDelegate.hashCode();
     }
@@ -3872,6 +3977,7 @@
     /*
      * @see java.util.List#get(int)
      */
+    @Override
     public PyInteger get(int index) {
         return listDelegate.get(index);
     }
@@ -3879,6 +3985,7 @@
     /*
      * @see java.util.List#set(int, java.lang.Object)
      */
+    @Override
     public PyInteger set(int index, PyInteger element) {
         return listDelegate.set(index, element);
     }
@@ -3886,6 +3993,7 @@
     /*
      * @see java.util.List#add(int, java.lang.Object)
      */
+    @Override
     public void add(int index, PyInteger element) {
         listDelegate.add(index, element);
     }
@@ -3893,6 +4001,7 @@
     /*
      * @see java.util.List#remove(int)
      */
+    @Override
     public PyInteger remove(int index) {
         return listDelegate.remove(index);
     }
@@ -3900,6 +4009,7 @@
     /*
      * @see java.util.List#indexOf(java.lang.Object)
      */
+    @Override
     public int indexOf(Object o) {
         return listDelegate.indexOf(o);
     }
@@ -3907,6 +4017,7 @@
     /*
      * @see java.util.List#lastIndexOf(java.lang.Object)
      */
+    @Override
     public int lastIndexOf(Object o) {
         return listDelegate.lastIndexOf(o);
     }
@@ -3914,6 +4025,7 @@
     /*
      * @see java.util.List#listIterator()
      */
+    @Override
     public ListIterator<PyInteger> listIterator() {
         return listDelegate.listIterator();
     }
@@ -3921,6 +4033,7 @@
     /*
      * @see java.util.List#listIterator(int)
      */
+    @Override
     public ListIterator<PyInteger> listIterator(int index) {
         return listDelegate.listIterator(index);
     }
@@ -3928,6 +4041,7 @@
     /*
      * @see java.util.List#subList(int, int)
      */
+    @Override
     public List<PyInteger> subList(int fromIndex, int toIndex) {
         return listDelegate.subList(fromIndex, toIndex);
     }
@@ -3986,8 +4100,8 @@
          * It is intended the client call this method only once to get the result of a series of
          * append operations. A second call to {@link #getCount()}, before any further appending,
          * returns a zero-length array. This is to ensure that the same array is not given out
-         * twice. However, {@link #getCount()} continues to return the number bytes accumuated until
-         * an append next occurs.
+         * twice. However, {@link #getCount()} continues to return the number bytes accumulated
+         * until an append next occurs.
          *
          * @return an array containing the accumulated result
          */
@@ -4131,7 +4245,7 @@
      * Every sub-class of BaseBytes overrides this method to return a <code>Builder<B></code>
      * where <code>B</code> is (normally) that class's particular type, and it extends
      * <code>Builder<B></code> so that {@link Builder#getResult()} produces an instance of
-     * <code>B</code> from the contents..
+     * <code>B</code> from the contents.
      *
      * @param capacity of the <code>Builder<B></code> returned
      * @return a <code>Builder<B></code> for the correct sub-class
diff --git a/src/org/python/core/BuiltinDocs.java b/src/org/python/core/BuiltinDocs.java
--- a/src/org/python/core/BuiltinDocs.java
+++ b/src/org/python/core/BuiltinDocs.java
@@ -2196,7 +2196,9 @@
         "Return a copy of the string S, where all characters occurring\n" + 
         "in the optional argument deletechars are removed, and the\n" + 
         "remaining characters have been mapped through the given\n" + 
-        "translation table, which must be a string of length 256.";
+        "translation table, which must be a string of length 256.\n" +
+        "If the table argument is None, no translation is applied and\n" +
+        "the operation simply removes the characters in deletechars.";
 
     public final static String str_upper_doc = 
         "S.upper() -> string\n" + 
diff --git a/src/org/python/core/PyBUF.java b/src/org/python/core/PyBUF.java
--- a/src/org/python/core/PyBUF.java
+++ b/src/org/python/core/PyBUF.java
@@ -3,20 +3,20 @@
 /**
  * This interface provides a base for the key interface of the buffer API, {@link PyBuffer},
  * including symbolic constants used by the consumer of a <code>PyBuffer</code> to specify its
- * requirements and assumptions. The Jython buffer API emulates the CPython buffer API.
+ * requirements and assumptions. The Jython buffer API emulates the CPython buffer API. There are
+ * two reasons for separating parts of <code>PyBuffer</code> into this interface:
  * <ul>
- * <li>There are two reasons for separating parts of <code>PyBuffer</code> into this interface: The
- * constants defined in CPython have the names <code>PyBUF_SIMPLE</code>,
+ * <li>The constants defined in CPython have the names <code>PyBUF_SIMPLE</code>,
  * <code>PyBUF_WRITABLE</code>, etc., and the trick of defining ours here means we can write
- * {@link PyBUF#SIMPLE}, {@link PyBUF#WRITABLE}, etc. so source code looks similar.</li>
+ * <code>PyBUF.SIMPLE</code>, <code>PyBUF.WRITABLE</code>, etc. so source code looks similar.</li>
  * <li>It is not so easy in Java as it is in C to treat a <code>byte</code> array as storing
  * anything other than <code>byte</code>, and we prepare for the possibility of buffers with a
  * series of different primitive types by defining here those methods that would be in common
- * between <code>(Byte)Buffer</code> and an assumed future <code>FloatBuffer</code> or
+ * between (Byte)<code>Buffer</code> and an assumed future <code>FloatBuffer</code> or
  * <code>TypedBuffer<T></code>. (Compare <code>java.nio.Buffer</code>.)</li>
  * </ul>
- * Except for other interfaces, it is unlikely any classes would implement <code>PyBUF</code>
- * directly. Users of the Jython buffer API can mostly overlook the distinction and just use
+ * It is unlikely any classes would implement <code>PyBUF</code>, except indirectly through other
+ * interfaces. Users of the Jython buffer API can mostly overlook the distinction and just use
  * <code>PyBuffer</code>.
  */
 public interface PyBUF {
@@ -39,12 +39,12 @@
 
     /**
      * An array reporting the size of the buffer, considered as a multidimensional array, in each
-     * dimension and (by its length) number of dimensions. The size is the size in "items". An item
-     * is the amount of buffer content addressed by one index or set of indices. In the simplest
-     * case an item is a single unit (byte), and there is one dimension. In complex cases, the array
-     * is multi-dimensional, and the item at each location is multi-unit (multi-byte). The consumer
-     * must not modify this array. A valid <code>shape</code> array is always returned (difference
-     * from CPython).
+     * dimension and (by its length) giving the number of dimensions. The size of the buffer is its
+     * size in "items". An item is the amount of buffer content addressed by one index or set of
+     * indices. In the simplest case an item is a single unit (byte), and there is one dimension. In
+     * complex cases, the array is multi-dimensional, and the item at each location is multi-unit
+     * (multi-byte). The consumer must not modify this array. A valid <code>shape</code> array is
+     * always returned (difference from CPython).
      *
      * @return the dimensions of the buffer as an array
      */
@@ -59,7 +59,7 @@
 
     /**
      * The total number of units (bytes) stored, which will be the product of the elements of the
-     * <code>shape</code> array, and the item size.
+     * <code>shape</code> array, and the item size in units.
      *
      * @return the total number of units stored.
      */
@@ -81,13 +81,13 @@
     /**
      * The <code>suboffsets</code> array is a further part of the support for interpreting the
      * buffer as an n-dimensional array of items, where the array potentially uses indirect
-     * addressing (like a real Java array of arrays, in fact). This is only applicable when there
-     * are more than 1 dimension and works in conjunction with the <code>strides</code> array. (More
+     * addressing (like a real Java array of arrays, in fact). This is only applicable when there is
+     * more than 1 dimension, and it works in conjunction with the <code>strides</code> array. (More
      * on this in the CPython documentation.) When used, <code>suboffsets[k]</code> is an integer
-     * index, bit a byte offset as in CPython. The consumer must not modify this array. When not
+     * index, not a byte offset as in CPython. The consumer must not modify this array. When not
      * needed for navigation <code>null</code> is returned (as in CPython).
      *
-     * @return suboffsets array or null in not necessary for navigation
+     * @return suboffsets array or <code>null</code> if not necessary for navigation
      */
     int[] getSuboffsets();
 
@@ -108,15 +108,15 @@
     static final int MAX_NDIM = 64;
     /**
      * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
-     * specify that it expects to write to the buffer contents. getBuffer will raise an exception if
-     * the exporter's buffer cannot meet this requirement.
+     * specify that it expects to write to the buffer contents. <code>getBuffer</code> will raise an
+     * exception if the exporter's buffer cannot meet this requirement.
      */
     static final int WRITABLE = 0x0001;
     /**
      * A constant used by the consumer in its call to {@link BufferProtocol#getBuffer(int)} to
      * specify that it assumes a simple one-dimensional organisation of the exported storage with
-     * item size of one. getBuffer will raise an exception if the consumer sets this flag and the
-     * exporter's buffer cannot be navigated that simply.
+     * item size of one. <code>getBuffer</code> will raise an exception if the consumer sets this
+     * flag and the exporter's buffer cannot be navigated that simply.
      */
     static final int SIMPLE = 0;
     /**
@@ -162,7 +162,7 @@
      * specify that it will assume a contiguous organisation of the units, but will enquire which
      * organisation it actually is.
      *
-     * getBuffer will raise an exception if the exporter's buffer is not contiguous.
+     * <code>getBuffer</code> will raise an exception if the exporter's buffer is not contiguous.
      * <code>ANY_CONTIGUOUS</code> implies <code>STRIDES</code>.
      */
     // Further CPython strangeness since it uses the strides array to answer the enquiry.
@@ -216,7 +216,7 @@
     /* Constants for readability, not standard for CPython */
 
     /**
-     * Field mask, use as in <code>if ((flags&NAVIGATION) == STRIDES) ...</code>. The importance of
+     * Field mask, used as in <code>if ((flags&NAVIGATION) == STRIDES) ...</code>. The importance of
      * the subset of flags defined by this mask is not so much in their "navigational" character as
      * in the way they are treated in a buffer request.
      * <p>
@@ -243,7 +243,7 @@
      */
     static final int IS_F_CONTIGUOUS = F_CONTIGUOUS & ~STRIDES;
     /**
-     * Field mask, use as in <code>if ((flags&CONTIGUITY)== ... ) ...</code>.
+     * Field mask, used as in <code>if ((flags&CONTIGUITY)== ... ) ...</code>.
      */
     static final int CONTIGUITY = (C_CONTIGUOUS | F_CONTIGUOUS | ANY_CONTIGUOUS) & ~STRIDES;
-}
\ No newline at end of file
+}
diff --git a/src/org/python/core/PyBuffer.java b/src/org/python/core/PyBuffer.java
--- a/src/org/python/core/PyBuffer.java
+++ b/src/org/python/core/PyBuffer.java
@@ -163,9 +163,9 @@
      * When a <code>PyBuffer</code> is the target, the same checks are carried out on the consumer
      * flags, and a return will normally be a reference to that buffer. A Jython
      * <code>PyBuffer</code> keeps count of these re-exports in order to match them with the number
-     * of calls to {@link #release()}. When the last matching release() arrives it is considered
-     * "final", and release actions may then take place on the exporting object. After the final
-     * release of a buffer, a call to <code>getBuffer</code> should raise an exception.
+     * of calls to {@link #release()}. When the last matching <code>release()</code> arrives it is
+     * considered "final", and release actions may then take place on the exporting object. After
+     * the final release of a buffer, a call to <code>getBuffer</code> should raise an exception.
      */
     @Override
     PyBuffer getBuffer(int flags) throws PyException;
@@ -213,16 +213,17 @@
      * <code>this.getPointer(i)</code>. A request for a slice where <code>start</code> <i>= s</i>,
      * <code>length</code> <i>= N</i> and <code>stride</code> <i>= m</i>, results in a buffer
      * <i>y</i> such that <i>y(k) = x(s+km)</i> where <i>k=0..(N-1)</i>. In Python terms, this is
-     * the slice <i>x[s : s+(N-1)m+1 : m]</i> (if m>0) or the slice <i>x[s : s+(N-1)m-1 : m]</i>
-     * (if m<0). Implementations should check that this range is entirely within the current
-     * buffer.
+     * the slice <i>x[s : s+(N-1)m+1 : m]</i> (if <i>m>0</i>) or the slice <i>x[s : s+(N-1)m-1 :
+     * m]</i> (if <i>m<0</i>). Implementations should check that this range is entirely within
+     * the current buffer.
      * <p>
      * In a simple buffer backed by a contiguous byte array, the result is a strided PyBuffer on the
      * same storage but where the offset is adjusted by <i>s</i> and the stride is as supplied. If
      * the current buffer is already strided and/or has an item size larger than single bytes, the
-     * new start index, length and stride will be translated from the arguments given, through this
-     * buffer's stride and item size. The consumer always expresses <code>start</code> and
-     * <code>strides</code> in terms of the abstract view of this buffer.
+     * new <code>start</code> index, <code>length</code> and <code>stride</code> will be translated
+     * from the arguments given, through this buffer's stride and item size. The caller always
+     * expresses <code>start</code> and <code>strides</code> in terms of the abstract view of this
+     * buffer.
      *
      * @param flags specifying features demanded and the navigational capabilities of the consumer
      * @param start index in the current buffer
@@ -265,13 +266,11 @@
      * Return a structure describing the slice of a byte array that holds the data being exported to
      * the consumer. For a one-dimensional contiguous buffer, assuming the following client code
      * where <code>obj</code> has type <code>BufferProtocol</code>:
-     *
      * <pre>
      * PyBuffer a = obj.getBuffer();
      * int itemsize = a.getItemsize();
      * PyBuffer.Pointer b = a.getBuf();
      * </pre>
-     *
      * the item with index <code>k</code> is in the array <code>b.storage</code> at index
      * <code>[b.offset + k*itemsize]</code> to <code>[b.offset + (k+1)*itemsize - 1]</code>
      * inclusive. And if <code>itemsize==1</code>, the item is simply the byte
@@ -287,17 +286,15 @@
     PyBuffer.Pointer getBuf();
 
     /**
-     * Return a structure describing the slice of a byte array that points to a single item from the
-     * data being exported to the consumer. For a one-dimensional contiguous buffer, assuming the
+     * Return a structure describing the position in a byte array of a single item from the data
+     * being exported to the consumer. For a one-dimensional contiguous buffer, assuming the
      * following client code where <code>obj</code> has type <code>BufferProtocol</code>:
-     *
      * <pre>
      * int k = ... ;
      * PyBuffer a = obj.getBuffer();
      * int itemsize = a.getItemsize();
      * PyBuffer.Pointer b = a.getPointer(k);
      * </pre>
-     *
      * the item with index <code>k</code> is in the array <code>b.storage</code> at index
      * <code>[b.offset]</code> to <code>[b.offset + itemsize - 1]</code> inclusive. And if
      * <code>itemsize==1</code>, the item is simply the byte <code>b.storage[b.offset]</code>
@@ -312,11 +309,10 @@
     PyBuffer.Pointer getPointer(int index);
 
     /**
-     * Return a structure describing the slice of a byte array that points to a single item from the
-     * data being exported to the consumer, in the case that array may be multi-dimensional. For a
+     * Return a structure describing the position in a byte array of a single item from the data
+     * being exported to the consumer, in the case that array may be multi-dimensional. For a
      * 3-dimensional contiguous buffer, assuming the following client code where <code>obj</code>
      * has type <code>BufferProtocol</code>:
-     *
      * <pre>
      * int i, j, k;
      * // ... calculation that assigns i, j, k
@@ -324,18 +320,17 @@
      * int itemsize = a.getItemsize();
      * PyBuffer.Pointer b = a.getPointer(i,j,k);
      * </pre>
-     *
      * the item with index <code>[i,j,k]</code> is in the array <code>b.storage</code> at index
      * <code>[b.offset]</code> to <code>[b.offset + itemsize - 1]</code> inclusive. And if
      * <code>itemsize==1</code>, the item is simply the byte <code>b.storage[b.offset]</code>
      * <p>
      * Essentially this is a method for computing the offset of a particular index. The client is
      * free to navigate the underlying buffer <code>b.storage</code> without respecting these
-     * boundaries.
-     * <p>
-     * If the buffer is also non-contiguous, <code>b.storage[b.offset]</code> is still the (first
-     * byte of) the item at index [0,...,0]. However, it is necessary to navigate <code>b</code>
-     * using the shape, strides and sub-offsets provided by the API.
+     * boundaries. If the buffer is non-contiguous, the above description is still valid (since a
+     * multi-byte item must itself be contiguously stored), but in any additional navigation of
+     * <code>b.storage[]</code> to other units, the client must use the shape, strides and
+     * sub-offsets provided by the API. Normally one starts <code>b = a.getBuf()</code> in order to
+     * establish the offset of index [0,...,0].
      *
      * @param indices multidimensional index at which to position the pointer
      * @return structure defining the byte[] slice that is the shared data
diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java
--- a/src/org/python/core/PyByteArray.java
+++ b/src/org/python/core/PyByteArray.java
@@ -12,26 +12,24 @@
 import org.python.expose.MethodType;
 
 /**
- * Partial implementation of Python bytearray. At the present stage of development, the class
- * provides:
- * <ul>
- * <li>constructors (both __init__ and the Java API constructors),</li>
- * <li>the slice operations (get, set and delete)</li>
- * <li>a <code>List<PyInteger></code> implementation for the Java API</li>
- * </ul>
- * and this is founded on a particular approach to storage management internally. However, the
- * implementation does not support the <code>memoryview</code> interface either for access or a a
- * source for its constructors although the signatures are present. The rich set of string-like
- * operations due a <code>bytearray</code> is not implemented.
- *
+ * Implementation of Python <code>bytearray</code> with a Java API that includes equivalents to most
+ * of the Python API. These Python equivalents accept a {@link PyObject} as argument, where you
+ * might have expected a <code>byte[]</code> or <code>PyByteArray</code>, in order to accommodate
+ * the full range of types accepted by the Python equivalent: usually, any <code>PyObject</code>
+ * that implements {@link BufferProtocol}, providing a one-dimensional array of bytes, is an
+ * acceptable argument. In the documentation, the reader will often see the terms "byte array" or
+ * "object viewable as bytes" instead of <code>bytearray</code> when this broader scope is intended.
+ * This may relate to parameters, or to the target object itself (in text that applies equally to
+ * base or sibling classes).
  */
 @ExposedType(name = "bytearray", base = PyObject.class, doc = BuiltinDocs.bytearray_doc)
 public class PyByteArray extends BaseBytes implements BufferProtocol {
 
+    /** The {@link PyType} of <code>bytearray</code>. */
     public static final PyType TYPE = PyType.fromClass(PyByteArray.class);
 
     /**
-     * Create a zero-length Python bytearray of explicitly-specified sub-type
+     * Constructs a zero-length Python <code>bytearray</code> of explicitly-specified sub-type
      *
      * @param type explicit Jython type
      */
@@ -40,16 +38,16 @@
     }
 
     /**
-     * Create a zero-length Python bytearray.
+     * Constructs a zero-length Python <code>bytearray</code>.
      */
     public PyByteArray() {
         super(TYPE);
     }
 
     /**
-     * Create zero-filled Python bytearray of specified size.
+     * Constructs zero-filled Python <code>bytearray</code> of specified size.
      *
-     * @param size of bytearray
+     * @param size of <code>bytearray</code>
      */
     public PyByteArray(int size) {
         super(TYPE);
@@ -57,7 +55,7 @@
     }
 
     /**
-     * Construct bytearray by copying values from int[].
+     * Constructs a <code>bytearray</code> by copying values from int[].
      *
      * @param value source of the bytes (and size)
      */
@@ -66,8 +64,8 @@
     }
 
     /**
-     * Create a new array filled exactly by a copy of the contents of the source, which is a
-     * bytearray (or bytes).
+     * Constructs a new array filled exactly by a copy of the contents of the source, which is a
+     * <code>bytearray</code> (or <code>bytes</code>).
      *
      * @param value source of the bytes (and size)
      */
@@ -77,7 +75,7 @@
     }
 
     /**
-     * Create a new array filled exactly by a copy of the contents of the source, which is a
+     * Constructs a new array filled exactly by a copy of the contents of the source, which is a
      * byte-oriented {@link PyBuffer}.
      *
      * @param value source of the bytes (and size)
@@ -88,7 +86,7 @@
     }
 
     /**
-     * Create a new array filled exactly by a copy of the contents of the source, which is an
+     * Constructs a new array filled exactly by a copy of the contents of the source, which is an
      * object supporting the Jython version of the PEP 3118 buffer API.
      *
      * @param value source of the bytes (and size)
@@ -99,7 +97,7 @@
     }
 
     /**
-     * Create a new array filled from an iterable of PyObject. The iterable must yield objects
+     * Constructs a new array filled from an iterable of PyObject. The iterable must yield objects
      * convertible to Python bytes (non-negative integers less than 256 or strings of length 1).
      *
      * @param value source of the bytes (and size)
@@ -110,8 +108,8 @@
     }
 
     /**
-     * Create a new array by encoding a PyString argument to bytes. If the PyString is actually a
-     * PyUnicode, the encoding must be explicitly specified.
+     * Constructs a new array by encoding a PyString argument to bytes. If the PyString is actually
+     * a PyUnicode, the encoding must be explicitly specified.
      *
      * @param arg primary argument from which value is taken
      * @param encoding name of optional encoding (must be a string type)
@@ -123,12 +121,12 @@
     }
 
     /**
-     * Create a new array by encoding a PyString argument to bytes. If the PyString is actually a
-     * PyUnicode, the encoding must be explicitly specified.
+     * Constructs a new array by encoding a PyString argument to bytes. If the PyString is actually
+     * a PyUnicode, the encoding must be explicitly specified.
      *
      * @param arg primary argument from which value is taken
-     * @param encoding name of optional encoding (may be null to select the default for this
-     *            installation)
+     * @param encoding name of optional encoding (may be <code>null</code> to select the default for
+     *            this installation)
      * @param errors name of optional errors policy
      */
     public PyByteArray(PyString arg, String encoding, String errors) {
@@ -137,8 +135,8 @@
     }
 
     /**
-     * Create a new array by encoding a PyString argument to bytes. If the PyString is actually a
-     * PyUnicode, an exception is thrown saying that the encoding must be explicitly specified.
+     * Constructs a new array by encoding a PyString argument to bytes. If the PyString is actually
+     * a PyUnicode, an exception is thrown saying that the encoding must be explicitly specified.
      *
      * @param arg primary argument from which value is taken
      */
@@ -148,7 +146,8 @@
     }
 
     /**
-     * Construct bytearray by re-using an array of byte as storage initialised by the client.
+     * Constructs a <code>bytearray</code> by re-using an array of byte as storage initialised by
+     * the client.
      *
      * @param storage pre-initialised with desired value: the caller should not keep a reference
      */
@@ -158,12 +157,13 @@
     }
 
     /**
-     * Construct bytearray by re-using an array of byte as storage initialised by the client.
+     * Constructs a <code>bytearray</code> by re-using an array of byte as storage initialised by
+     * the client.
      *
      * @param storage pre-initialised with desired value: the caller should not keep a reference
      * @param size number of bytes actually used
-     * @throws IllegalArgumentException if the range [0:size] is not within the array bounds of
-     *             the storage.
+     * @throws IllegalArgumentException if the range [0:size] is not within the array bounds of the
+     *             storage.
      */
     PyByteArray(byte[] storage, int size) {
         super(TYPE);
@@ -171,28 +171,26 @@
     }
 
     /**
-     * Create a new bytearray object from an arbitrary Python object according to the same rules as
-     * apply in Python to the bytearray() constructor:
+     * Constructs a new <code>bytearray</code> object from an arbitrary Python object according to
+     * the same rules as apply in Python to the <code>bytearray()</code> constructor:
      * <ul>
-     * <li>bytearray() Construct a zero-length bytearray (arg is null).</li>
-     * <li>bytearray(int) Construct a zero-initialized bytearray of the given length.</li>
-     * <li>bytearray(iterable_of_ints) Construct from iterable yielding integers in [0..255]</li>
-     * <li>bytearray(string [, encoding [, errors] ]) Construct from a text string, optionally using
-     * the specified encoding.</li>
-     * <li>bytearray(unicode, encoding [, errors]) Construct from a unicode string using the
-     * specified encoding.</li>
-     * <li>bytearray(bytes_or_bytearray) Construct as a mutable copy of bytes or existing bytearray
-     * object.</li>
+     * <li><code>bytearray()</code> Construct a zero-length <code>bytearray</code>.</li>
+     * <li><code>bytearray(int)</code> Construct a zero-initialized <code>bytearray</code> of the
+     * given length.</li>
+     * <li><code>bytearray(iterable_of_ints)</code> Construct from iterable yielding integers in
+     * [0..255]</li>
+     * <li><code>bytearray(buffer)</code> Construct by reading from any object implementing
+     * {@link BufferProtocol}, including <code>str/bytes</code> or another <code>bytearray</code>.</li>
      * </ul>
      * When it is necessary to specify an encoding, as in the Python signature
-     * <code>bytearray(string, encoding[, errors])</code>, use the constructor
-     * {@link #PyByteArray(PyString, String, String)}. If the PyString is actually a PyUnicode, an
-     * encoding must be specified, and using this constructor will throw an exception about that.
+     * <code>bytearray(string, encoding [, errors])</code>, use the constructor
+     * {@link #PyByteArray(PyString, String, String)}. If the <code>PyString</code> is actually a
+     * <code>PyUnicode</code>, an encoding must be specified, and using this constructor will throw
+     * an exception about that.
      *
-     * @param arg primary argument from which value is taken (may be null)
-     * @throws PyException in the same circumstances as bytearray(arg), TypeError for non-iterable,
-     *             non-integer argument type, and ValueError if iterables do not yield byte [0..255]
-     *             values.
+     * @param arg primary argument from which value is taken (may be <code>null</code>)
+     * @throws PyException (TypeError) for non-iterable,
+     * @throws PyException (ValueError) if iterables do not yield byte [0..255] values.
      */
     public PyByteArray(PyObject arg) throws PyException {
         super(TYPE);
@@ -284,18 +282,19 @@
         }
     }
 
-    /* ============================================================================================
+    /*
+     * ============================================================================================
      * API for org.python.core.PySequence
      * ============================================================================================
      */
 
     /**
-     * Returns a slice of elements from this sequence as a PyByteArray.
+     * Returns a slice of elements from this sequence as a <code>PyByteArray</code>.
      *
      * @param start the position of the first element.
      * @param stop one more than the position of the last element.
      * @param step the step size.
-     * @return a PyByteArray corresponding the the given range of elements.
+     * @return a <code>PyByteArray</code> corresponding the the given range of elements.
      */
     @Override
     protected synchronized PyByteArray getslice(int start, int stop, int step) {
@@ -333,8 +332,8 @@
     }
 
     /**
-     * Returns a PyByteArray that repeats this sequence the given number of times, as in the
-     * implementation of <tt>__mul__</tt> for strings.
+     * Returns a <code>PyByteArray</code> that repeats this sequence the given number of times, as
+     * in the implementation of <tt>__mul__</tt> for strings.
      *
      * @param count the number of times to repeat this.
      * @return this byte array repeated count times.
@@ -347,8 +346,8 @@
     }
 
     /**
-     * Replace the contents of this PyByteArray with the given number of repeats of the original
-     * contents, as in the implementation of <tt>__mul__</tt> for strings.
+     * Replace the contents of this <code>PyByteArray</code> with the given number of repeats of the
+     * original contents, as in the implementation of <tt>__mul__</tt> for strings.
      *
      * @param count the number of times to repeat this.
      */
@@ -357,35 +356,36 @@
     }
 
     /**
-     * Sets the indexed element of the bytearray to the given value. This is an extension point
-     * called by PySequence in its implementation of {@link #__setitem__} It is guaranteed by
-     * PySequence that the index is within the bounds of the array. Any other clients calling
-     * <tt>pyset(int)</tt> must make the same guarantee.
+     * Sets the indexed element of the <code>bytearray</code> to the given value. This is an
+     * extension point called by PySequence in its implementation of {@link #__setitem__} It is
+     * guaranteed by PySequence that the index is within the bounds of the array. Any other clients
+     * calling <code>pyset(int)</code> must make the same guarantee.
      *
      * @param index index of the element to set.
      * @param value the value to set this element to.
-     * @throws PyException(AttributeError) if value cannot be converted to an integer
-     * @throws PyException(ValueError) if value<0 or value>255
+     * @throws PyException (AttributeError) if value cannot be converted to an integer
+     * @throws PyException (ValueError) if value<0 or value>255
      */
+    @Override
     public synchronized void pyset(int index, PyObject value) throws PyException {
         storage[index + offset] = byteCheck(value);
     }
 
     /**
-     * Insert the element (interpreted as a Python byte value) at the given index.
-     * Python int, long and string types of length 1 are allowed.
+     * Insert the element (interpreted as a Python byte value) at the given index. Python
+     * <code>int</code>, <code>long</code> and <code>str</code> types of length 1 are allowed.
      *
      * @param index to insert at
      * @param element to insert (by value)
-     * @throws PyException(IndexError) if the index is outside the array bounds
-     * @throws PyException(ValueError) if element<0 or element>255
-     * @throws PyException(TypeError) if the subclass is immutable
+     * @throws PyException (IndexError) if the index is outside the array bounds
+     * @throws PyException (ValueError) if element<0 or element>255
+     * @throws PyException (TypeError) if the subclass is immutable
      */
     @Override
     public synchronized void pyinsert(int index, PyObject element) {
         // Open a space at the right location.
         storageReplace(index, 0, 1);
-        storage[offset+index] = byteCheck(element);
+        storage[offset + index] = byteCheck(element);
     }
 
     /**
@@ -398,9 +398,10 @@
      * of exactly that many elements (or convertible to such a sequence).
      * <p>
      * When assigning from a sequence type or iterator, the sequence may contain arbitrary
-     * <code>PyObject</code>s, but acceptable ones are PyInteger, PyLong or PyString of length 1. If
-     * any one of them proves unsuitable for assignment to a Python bytarray element, an exception
-     * is thrown and this bytearray is unchanged.
+     * {@link PyObject}s, but acceptable ones are {@link PyInteger}, {@link PyLong} or
+     * {@link PyString} of length 1. If any one of them proves unsuitable for assignment to a Python
+     * <code>bytearray</code> element, an exception is thrown and this <code>bytearray</code> is
+     * unchanged.
      *
      * <pre>
      * a = bytearray(b'abcdefghijklmnopqrst')
@@ -472,14 +473,14 @@
 
     /**
      * Sets the given range of elements according to Python slice assignment semantics from a
-     * zero-filled bytearray of the given length.
+     * zero-filled <code>bytearray</code> of the given length.
      *
      * @see #setslice(int, int, int, PyObject)
      * @param start the position of the first element.
      * @param stop one more than the position of the last element.
      * @param step the step size.
      * @param len number of zeros to insert consistent with the slice assignment
-     * @throws PyException(SliceSizeError) if the value size is inconsistent with an extended slice
+     * @throws PyException (SliceSizeError) if the value size is inconsistent with an extended slice
      */
     private void setslice(int start, int stop, int step, int len) throws PyException {
         if (step == 1) {
@@ -501,29 +502,36 @@
 
     /**
      * Sets the given range of elements according to Python slice assignment semantics from a
-     * PyString.
+     * {@link PyString} that is not a {@link PyUnicode}.
      *
      * @see #setslice(int, int, int, PyObject)
      * @param start the position of the first element.
      * @param stop one more than the position of the last element.
      * @param step the step size.
      * @param value a PyString object consistent with the slice assignment
-     * @throws PyException(SliceSizeError) if the value size is inconsistent with an extended slice
+     * @throws PyException (SliceSizeError) if the value size is inconsistent with an extended slice
+     * @throws PyException (ValueError) if the value is a <code>PyUnicode</code>
      */
     private void setslice(int start, int stop, int step, PyString value) throws PyException {
-        String v = value.asString();
-        int len = v.length();
-        if (step == 1) {
-            // Delete this[start:stop] and open a space of the right size
-            storageReplace(start, stop - start, len);
-            setBytes(start, v);
+        if (value instanceof PyUnicode) {
+            // Has to be 8-bit PyString
+            throw Py.TypeError("can't set bytearray slice from unicode");
         } else {
-            // This is an extended slice which means we are replacing elements
-            int n = sliceLength(start, stop, step);
-            if (n != len) {
-                throw SliceSizeError("bytes", len, n);
+            // Assignment is from 8-bit data
+            String v = value.asString();
+            int len = v.length();
+            if (step == 1) {
+                // Delete this[start:stop] and open a space of the right size
+                storageReplace(start, stop - start, len);
+                setBytes(start, v);
+            } else {
+                // This is an extended slice which means we are replacing elements
+                int n = sliceLength(start, stop, step);
+                if (n != len) {
+                    throw SliceSizeError("bytes", len, n);
+                }
+                setBytes(start, step, v);
             }
-            setBytes(start, step, v);
         }
     }
 
@@ -536,7 +544,7 @@
      * @param stop one more than the position of the last element.
      * @param step the step size.
      * @param value an object supporting the buffer API consistent with the slice assignment
-     * @throws PyException(SliceSizeError) if the value size is inconsistent with an extended slice
+     * @throws PyException (SliceSizeError) if the value size is inconsistent with an extended slice
      */
     private void setslice(int start, int stop, int step, BufferProtocol value) throws PyException {
 
@@ -547,7 +555,7 @@
         if (step == 1) {
             // Delete this[start:stop] and open a space of the right size
             storageReplace(start, stop - start, len);
-            view.copyTo(storage, start+offset);
+            view.copyTo(storage, start + offset);
 
         } else {
             // This is an extended slice which means we are replacing elements
@@ -564,14 +572,14 @@
 
     /**
      * Sets the given range of elements according to Python slice assignment semantics from a
-     * bytearray (or bytes).
+     * <code>bytearray</code> (or bytes).
      *
      * @see #setslice(int, int, int, PyObject)
      * @param start the position of the first element.
      * @param stop one more than the position of the last element.
      * @param step the step size.
-     * @param value a bytearray (or bytes) object consistent with the slice assignment
-     * @throws PyException(SliceSizeError) if the value size is inconsistent with an extended slice
+     * @param value a <code>bytearray</code> (or bytes) object consistent with the slice assignment
+     * @throws PyException (SliceSizeError) if the value size is inconsistent with an extended slice
      */
     private void setslice(int start, int stop, int step, BaseBytes value) throws PyException {
 
@@ -603,14 +611,14 @@
 
     /**
      * Sets the given range of elements according to Python slice assignment semantics from a
-     * bytearray (or bytes).
+     * <code>bytearray</code> (or bytes).
      *
      * @see #setslice(int, int, int, PyObject)
      * @param start the position of the first element.
      * @param stop one more than the position of the last element.
      * @param step the step size.
      * @param iter iterable source of values to enter in the array
-     * @throws PyException(SliceSizeError) if the iterable size is inconsistent with an extended
+     * @throws PyException (SliceSizeError) if the iterable size is inconsistent with an extended
      *             slice
      */
     private void setslice(int start, int stop, int step, Iterable<? extends PyObject> iter) {
@@ -640,21 +648,12 @@
         }
     }
 
-// Idiom:
-// if (step == 1) {
-// // Do something efficient with block start...stop-1
-// } else {
-// int n = sliceLength(start, stop, step);
-// for (int i = start, j = 0; j < n; i += step, j++) {
-// // Perform jth operation with element i
-// }
-// }
-
     /*
      * Deletes an element from the sequence (and closes up the gap).
      *
      * @param index index of the element to delete.
      */
+    @Override
     protected synchronized void del(int index) {
         // XXX Change SequenceIndexDelegate to avoid repeated calls to del(int) for extended slice
         storageDelete(index, 1);
@@ -667,6 +666,7 @@
      *
      * @param stop one more than the position of the last element.
      */
+    @Override
     protected synchronized void delRange(int start, int stop) {
         storageDelete(start, stop - start);
     }
@@ -714,29 +714,29 @@
     }
 
     /**
-     * Initialise a mutable bytearray object from various arguments. This single initialisation must
-     * support:
+     * Initialise a mutable <code>bytearray</code> object from various arguments. This single
+     * initialisation must support:
      * <ul>
-     * <li>bytearray() Construct a zero-length bytearray.</li>
-     * <li>bytearray(int) Construct a zero-initialized bytearray of the given length.</li>
-     * <li>bytearray(iterable_of_ints) Construct from iterable yielding integers in [0..255]</li>
-     * <li>bytearray(string [, encoding [, errors] ]) Construct from a text string, optionally using
+     * <li><code>bytearray()</code> Construct a zero-length <code>bytearray</code>.</li>
+     * <li><code>bytearray(int)</code> Construct a zero-initialized <code>bytearray</code> of the
+     * given length.</li>
+     * <li><code>bytearray(iterable_of_ints)</code> Construct from iterable yielding integers in
+     * [0..255]</li>
+     * <li><code>bytearray(buffer)</code> Construct by reading from any object implementing
+     * {@link BufferProtocol}, including <code>str/bytes</code> or another <code>bytearray</code>.</li>
+     * <li><code>bytearray(string, encoding [, errors])</code> Construct from a
+     * <code>str/bytes</code>, decoded using the system default encoding, and encoded to bytes using
      * the specified encoding.</li>
-     * <li>bytearray(unicode, encoding [, errors]) Construct from a unicode string using the
-     * specified encoding.</li>
-     * <li>bytearray(bytes_or_bytearray) Construct as a mutable copy of bytes or existing bytearray
-     * object.</li>
+     * <li><code>bytearray(unicode, encoding [, errors])</code> Construct from a
+     * <code>unicode</code> string, encoded to bytes using the specified encoding.</li>
      * </ul>
-     * Unlike CPython we are not able to support the initialisation: <li>bytearray(memory_view)
-     * Construct as copy of any object implementing the buffer API.</li> </ul> Although effectively
-     * a constructor, it is possible to call __init__ on a 'used' object so the method does not
-     * assume any particular prior state.
+     * Although effectively a constructor, it is possible to call <code>__init__</code> on a 'used'
+     * object so the method does not assume any particular prior state.
      *
      * @param args argument array according to Jython conventions
      * @param kwds Keywords according to Jython conventions
-     * @throws PyException in the same circumstances as bytearray(arg), TypeError for non-iterable,
-     *             non-integer argument type, and ValueError if iterables do not yield byte [0..255]
-     *             values.
+     * @throws PyException (TypeError) for non-iterable,
+     * @throws PyException (ValueError) if iterables do not yield byte [0..255] values.
      */
     @ExposedNew
     @ExposedMethod(doc = BuiltinDocs.bytearray___init___doc)
@@ -795,7 +795,8 @@
         };
     }
 
-    /* ============================================================================================
+    /*
+     * ============================================================================================
      * Python API rich comparison operations
      * ============================================================================================
      */
@@ -830,8 +831,6 @@
         return basebytes___gt__(other);
     }
 
-
-
     @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___eq___doc)
     final synchronized PyObject bytearray___eq__(PyObject other) {
         return basebytes___eq__(other);
@@ -862,7 +861,8 @@
         return basebytes___gt__(other);
     }
 
-/* ============================================================================================
+/*
+ * ============================================================================================
  * Python API for bytearray
  * ============================================================================================
  */
@@ -876,10 +876,8 @@
     final synchronized PyObject bytearray___add__(PyObject o) {
         PyByteArray sum = null;
 
-
         // XXX re-write using buffer API
 
-
         if (o instanceof BaseBytes) {
             BaseBytes ob = (BaseBytes)o;
             // Quick route: allocate the right size bytearray and copy the two parts in.
@@ -985,7 +983,7 @@
      * length 1.
      *
      * @param element the item to append.
-     * @throws PyException(ValueError) if element<0 or element>255
+     * @throws PyException (ValueError) if element<0 or element>255
      */
     public void append(PyObject element) {
         bytearray_append(element);
@@ -1002,9 +1000,10 @@
      * Implement to the standard Python __contains__ method, which in turn implements the
      * <code>in</code> operator.
      *
-     * @param o the element to search for in this bytearray.
+     * @param o the element to search for in this <code>bytearray</code>.
      * @return the result of the search.
      **/
+    @Override
     public boolean __contains__(PyObject o) {
         return basebytes___contains__(o);
     }
@@ -1038,7 +1037,7 @@
      * <code>width</code> is less than <code>this.size()</code>.
      *
      * @param width desired
-     * @param fillchar one-byte String to fill with, or null implying space
+     * @param fillchar one-byte String to fill with, or <code>null</code> implying space
      * @return new byte array containing the result
      */
     public PyByteArray center(int width, String fillchar) {
@@ -1097,11 +1096,11 @@
      * Implementation of Python <code>endswith(suffix)</code>.
      *
      * When <code>suffix</code> is of a type that may be treated as an array of bytes, return
-     * <code>true</code> if and only if this bytearray ends with the <code>suffix</code>.
-     * <code>suffix</code> can also be a tuple of suffixes to look for.
+     * <code>true</code> if and only if this <code>bytearray</code> ends with the
+     * <code>suffix</code>. <code>suffix</code> can also be a tuple of suffixes to look for.
      *
      * @param suffix byte array to match, or object viewable as such, or a tuple of them
-     * @return true if and only if this bytearray ends with the suffix (or one of them)
+     * @return true if and only if this <code>bytearray</code> ends with the suffix (or one of them)
      */
     public boolean endswith(PyObject suffix) {
         return basebytes_starts_or_endswith(suffix, null, null, true);
@@ -1111,13 +1110,14 @@
      * Implementation of Python <code>endswith( suffix [, start ] )</code>.
      *
      * When <code>suffix</code> is of a type that may be treated as an array of bytes, return
-     * <code>true</code> if and only if this bytearray ends with the <code>suffix</code>.
-     * <code>suffix</code> can also be a tuple of suffixes to look for. With optional
-     * <code>start</code> (which may be <code>null</code> or <code>Py.None</code>), define the
-     * effective bytearray to be the slice <code>[start:]</code> of this bytearray.
+     * <code>true</code> if and only if this <code>bytearray</code> ends with the
+     * <code>suffix</code>. <code>suffix</code> can also be a tuple of suffixes to look for. With
+     * optional <code>start</code> (which may be <code>null</code> or <code>Py.None</code>), define
+     * the effective <code>bytearray</code> to be the slice <code>[start:]</code> of this
+     * <code>bytearray</code>.
      *
      * @param suffix byte array to match, or object viewable as such, or a tuple of them
-     * @param start of slice in this bytearray to match
+     * @param start of slice in this <code>bytearray</code> to match
      * @return true if and only if this[start:] ends with the suffix (or one of them)
      */
     public boolean endswith(PyObject suffix, PyObject start) {
@@ -1128,15 +1128,15 @@
      * Implementation of Python <code>endswith( suffix [, start [, end ]] )</code>.
      *
      * When <code>suffix</code> is of a type that may be treated as an array of bytes, return
-     * <code>true</code> if and only if this bytearray ends with the <code>suffix</code>.
-     * <code>suffix</code> can also be a tuple of suffixes to look for. With optional
-     * <code>start</code> and <code>end</code> (which may be <code>null</code> or
-     * <code>Py.None</code>), define the effective bytearray to be the slice
-     * <code>[start:end]</code> of this bytearray.
+     * <code>true</code> if and only if this <code>bytearray</code> ends with the
+     * <code>suffix</code>. <code>suffix</code> can also be a tuple of suffixes to look for. With
+     * optional <code>start</code> and <code>end</code> (which may be <code>null</code> or
+     * <code>Py.None</code>), define the effective <code>bytearray</code> to be the slice
+     * <code>[start:end]</code> of this <code>bytearray</code>.
      *
      * @param suffix byte array to match, or object viewable as such, or a tuple of them
-     * @param start of slice in this bytearray to match
-     * @param end of slice in this bytearray to match
+     * @param start of slice in this <code>bytearray</code> to match
+     * @param end of slice in this <code>bytearray</code> to match
      * @return true if and only if this[start:end] ends with the suffix (or one of them)
      */
     public boolean endswith(PyObject suffix, PyObject start, PyObject end) {
@@ -1180,7 +1180,7 @@
 
     /**
      * Append the elements in the argument sequence to the end of the array, equivalent to:
-     * <code>s[len(s):len(s)] = o</code>. The argument must be a subclass of BaseBytes or an
+     * <code>s[len(s):len(s)] = o</code>. The argument must be a subclass of {@link BaseBytes} or an
      * iterable type returning elements compatible with byte assignment.
      *
      * @param o the sequence of items to append to the list.
@@ -1192,7 +1192,7 @@
     @ExposedMethod(doc = BuiltinDocs.bytearray_extend_doc)
     final synchronized void bytearray_extend(PyObject o) {
         // Use the general method, assigning to the crack at the end of the array.
-        // Note this deals with all legitimate PyObject types and the case o==this.
+        // Note this deals with all legitimate PyObject types including the case o==this.
         setslice(size, size, 1, o);
     }
 
@@ -1253,7 +1253,7 @@
      * </pre>
      *
      * @param hex specification of the bytes
-     * @throws PyException(ValueError) if non-hex characters, or isolated ones, are encountered
+     * @throws PyException (ValueError) if non-hex characters, or isolated ones, are encountered
      */
     static PyByteArray fromhex(String hex) throws PyException {
         return bytearray_fromhex(TYPE, hex);
@@ -1322,9 +1322,11 @@
 
     /**
      * This type is not hashable.
+     *
+     * @throws PyException (TypeError) as this type is not hashable.
      */
     @Override
-    public int hashCode() {
+    public int hashCode() throws PyException {
         return bytearray___hash__();
     }
 
@@ -1443,10 +1445,10 @@
         return (PyByteArray)basebytes_upper();
     }
 
-   /**
-     * Implementation of Python <code>join(iterable)</code>. Return a bytearray which is the
-     * concatenation of the byte arrays in the iterable <code>iterable</code>. The separator between
-     * elements is the byte array providing this method.
+    /**
+     * Implementation of Python <code>join(iterable)</code>. Return a <code>bytearray</code> which
+     * is the concatenation of the byte arrays in the iterable <code>iterable</code>. The separator
+     * between elements is the byte array providing this method.
      *
      * @param iterable of byte array objects, or objects viewable as such.
      * @return byte array produced by concatenation.
@@ -1484,7 +1486,7 @@
      * <code>width</code> is less than <code>this.size()</code>.
      *
      * @param width desired
-     * @param fillchar one-byte String to fill with, or null implying space
+     * @param fillchar one-byte String to fill with, or <code>null</code> implying space
      * @return new byte array containing the result
      */
     public PyByteArray ljust(int width, String fillchar) {
@@ -1498,8 +1500,8 @@
     }
 
     /**
-     * Implementation of Python <code>lstrip()</code>. Return a copy of the byte array with the leading
-     * whitespace characters removed.
+     * Implementation of Python <code>lstrip()</code>. Return a copy of the byte array with the
+     * leading whitespace characters removed.
      *
      * @return a byte array containing this value stripped of those bytes
      */
@@ -1510,10 +1512,10 @@
     /**
      * Implementation of Python <code>lstrip(bytes)</code>
      *
-     * Return a copy of the byte array with the leading characters removed. The bytes
-     * argument is an object specifying the set of characters to be removed. If null or None, the
-     * bytes argument defaults to removing whitespace. The bytes argument is not a prefix;
-     * rather, all combinations of its values are stripped.
+     * Return a copy of the byte array with the leading characters removed. The bytes argument is an
+     * object specifying the set of characters to be removed. If <code>null</code> or
+     * <code>None</code>, the bytes argument defaults to removing whitespace. The bytes argument is
+     * not a prefix; rather, all combinations of its values are stripped.
      *
      * @param bytes treated as a set of bytes defining what values to strip
      * @return a byte array containing this value stripped of those bytes (at the left)
@@ -1542,7 +1544,8 @@
     }
 
     /**
-     * Removes and return the last element in the byte array.
+     * Remove and return the last element in the byte array.
+     *
      * @return PyInteger representing the value
      */
     public PyInteger pop() {
@@ -1584,7 +1587,7 @@
      * argument must be a PyInteger, PyLong or string of length 1.
      *
      * @param o the value to remove from the list.
-     * @throws PyException ValueError if o not found in bytearray
+     * @throws PyException ValueError if o not found in <code>bytearray</code>
      */
     public void remove(PyObject o) throws PyException {
         bytearray_remove(o);
@@ -1746,7 +1749,7 @@
      * <code>width</code> is less than <code>this.size()</code>.
      *
      * @param width desired
-     * @param fillchar one-byte String to fill with, or null implying space
+     * @param fillchar one-byte String to fill with, or <code>null</code> implying space
      * @return new byte array containing the result
      */
     public PyByteArray rjust(int width, String fillchar) {
@@ -1793,7 +1796,8 @@
     }
 
     /**
-     * Implementation of Python <code>rstrip()</code>. Return a copy of the byte array with the trailing whitespace characters removed.
+     * Implementation of Python <code>rstrip()</code>. Return a copy of the byte array with the
+     * trailing whitespace characters removed.
      *
      * @return a byte array containing this value stripped of those bytes (at right)
      */
@@ -1804,10 +1808,10 @@
     /**
      * Implementation of Python <code>rstrip(bytes)</code>
      *
-     * Return a copy of the byte array with the trailing characters removed. The bytes
-     * argument is an object specifying the set of characters to be removed. If null or None, the
-     * bytes argument defaults to removing whitespace. The bytes argument is not a suffix;
-     * rather, all combinations of its values are stripped.
+     * Return a copy of the byte array with the trailing characters removed. The bytes argument is
+     * an object specifying the set of characters to be removed. If <code>null</code> or
+     * <code>None</code>, the bytes argument defaults to removing whitespace. The bytes argument is
+     * not a suffix; rather, all combinations of its values are stripped.
      *
      * @param bytes treated as a set of bytes defining what values to strip
      * @return a byte array containing this value stripped of those bytes (at right)
@@ -1844,11 +1848,12 @@
      * Implementation of Python <code>startswith(prefix)</code>.
      *
      * When <code>prefix</code> is of a type that may be treated as an array of bytes, return
-     * <code>true</code> if and only if this bytearray starts with the <code>prefix</code>.
-     * <code>prefix</code> can also be a tuple of prefixes to look for.
+     * <code>true</code> if and only if this <code>bytearray</code> starts with the
+     * <code>prefix</code>. <code>prefix</code> can also be a tuple of prefixes to look for.
      *
      * @param prefix byte array to match, or object viewable as such, or a tuple of them
-     * @return true if and only if this bytearray starts with the prefix (or one of them)
+     * @return true if and only if this <code>bytearray</code> starts with the prefix (or one of
+     *         them)
      */
     public boolean startswith(PyObject prefix) {
         return basebytes_starts_or_endswith(prefix, null, null, false);
@@ -1858,13 +1863,14 @@
      * Implementation of Python <code>startswith( prefix [, start ] )</code>.
      *
      * When <code>prefix</code> is of a type that may be treated as an array of bytes, return
-     * <code>true</code> if and only if this bytearray starts with the <code>prefix</code>.
-     * <code>prefix</code> can also be a tuple of prefixes to look for. With optional
-     * <code>start</code> (which may be <code>null</code> or <code>Py.None</code>), define the
-     * effective bytearray to be the slice <code>[start:]</code> of this bytearray.
+     * <code>true</code> if and only if this <code>bytearray</code> starts with the
+     * <code>prefix</code>. <code>prefix</code> can also be a tuple of prefixes to look for. With
+     * optional <code>start</code> (which may be <code>null</code> or <code>Py.None</code>), define
+     * the effective <code>bytearray</code> to be the slice <code>[start:]</code> of this
+     * <code>bytearray</code>.
      *
      * @param prefix byte array to match, or object viewable as such, or a tuple of them
-     * @param start of slice in this bytearray to match
+     * @param start of slice in this <code>bytearray</code> to match
      * @return true if and only if this[start:] starts with the prefix (or one of them)
      */
     public boolean startswith(PyObject prefix, PyObject start) {
@@ -1875,15 +1881,15 @@
      * Implementation of Python <code>startswith( prefix [, start [, end ]] )</code>.
      *
      * When <code>prefix</code> is of a type that may be treated as an array of bytes, return
-     * <code>true</code> if and only if this bytearray starts with the <code>prefix</code>.
-     * <code>prefix</code> can also be a tuple of prefixes to look for. With optional
-     * <code>start</code> and <code>end</code> (which may be <code>null</code> or
-     * <code>Py.None</code>), define the effective bytearray to be the slice
-     * <code>[start:end]</code> of this bytearray.
+     * <code>true</code> if and only if this <code>bytearray</code> starts with the
+     * <code>prefix</code>. <code>prefix</code> can also be a tuple of prefixes to look for. With
+     * optional <code>start</code> and <code>end</code> (which may be <code>null</code> or
+     * <code>Py.None</code>), define the effective <code>bytearray</code> to be the slice
+     * <code>[start:end]</code> of this <code>bytearray</code>.
      *
      * @param prefix byte array to match, or object viewable as such, or a tuple of them
-     * @param start of slice in this bytearray to match
-     * @param end of slice in this bytearray to match
+     * @param start of slice in this <code>bytearray</code> to match
+     * @param end of slice in this <code>bytearray</code> to match
      * @return true if and only if this[start:end] starts with the prefix (or one of them)
      */
     public boolean startswith(PyObject prefix, PyObject start, PyObject end) {
@@ -1896,8 +1902,8 @@
     }
 
     /**
-     * Implementation of Python <code>strip()</code>. Return a copy of the byte array with the leading
-     * and trailing whitespace characters removed.
+     * Implementation of Python <code>strip()</code>. Return a copy of the byte array with the
+     * leading and trailing whitespace characters removed.
      *
      * @return a byte array containing this value stripped of those bytes (left and right)
      */
@@ -1909,9 +1915,10 @@
      * Implementation of Python <code>strip(bytes)</code>
      *
      * Return a copy of the byte array with the leading and trailing characters removed. The bytes
-     * argument is anbyte arrayt specifying the set of characters to be removed. If null or None, the
-     * bytes argument defaults to removing whitespace. The bytes argument is not a prefix or suffix;
-     * rather, all combinations of its values are stripped.
+     * argument is anbyte arrayt specifying the set of characters to be removed. If
+     * <code>null</code> or <code>None</code>, the bytes argument defaults to removing whitespace.
+     * The bytes argument is not a prefix or suffix; rather, all combinations of its values are
+     * stripped.
      *
      * @param bytes treated as a set of bytes defining what values to strip
      * @return a byte array containing this value stripped of those bytes (left and right)
@@ -1980,13 +1987,13 @@
      * Implementation of Python <code>translate(table).</code>
      *
      * Return a copy of the byte array where all bytes occurring in the optional argument
-     * <code>deletechars</code> are removed, and the remaining bytes have been mapped through the given
-     * translation table, which must be of length 256.
+     * <code>deletechars</code> are removed, and the remaining bytes have been mapped through the
+     * given translation table, which must be of length 256.
      *
      * @param table length 256 translation table (of a type that may be regarded as a byte array)
      * @return translated byte array
      */
-     public PyByteArray translate(PyObject table) {
+    public PyByteArray translate(PyObject table) {
         return bytearray_translate(table, null);
     }
 
@@ -1994,12 +2001,12 @@
      * Implementation of Python <code>translate(table[, deletechars]).</code>
      *
      * Return a copy of the byte array where all bytes occurring in the optional argument
-     * <code>deletechars</code> are removed, and the remaining bytes have been mapped through the given
-     * translation table, which must be of length 256.
+     * <code>deletechars</code> are removed, and the remaining bytes have been mapped through the
+     * given translation table, which must be of length 256.
      *
-     * You can use the maketrans() helper function in the string module to create a translation
-     * table. For string objects, set the table argument to None for translations that only delete
-     * characters:
+     * You can use the Python <code>maketrans()</code> helper function in the <code>string</code>
+     * module to create a translation table. For string objects, set the table argument to
+     * <code>None</code> for translations that only delete characters:
      *
      * @param table length 256 translation table (of a type that may be regarded as a byte array)
      * @param deletechars object that may be regarded as a byte array, defining bytes to delete
@@ -2131,6 +2138,7 @@
      *
      * @param needed becomes the new value of this.size
      */
+    @Override
     protected void newStorage(int needed) {
         if (needed > 0) {
             final int L = recLength(needed);
@@ -2168,7 +2176,7 @@
      * been preserved, although probably moved, and the gap between them has been adjusted to the
      * requested size.
      * <p>
-     * The effect on this PyByteArray is that:
+     * The effect on this <code>PyByteArray</code> is that:
      *
      * <pre>
      * this.offset = f'
@@ -2277,7 +2285,8 @@
      * where the regions of length <code>a</code> and <code>b=size-(a+d)</code> have been preserved
      * and the gap between them adjusted to specification. The new offset f' is chosen heuristically
      * by the method to optimise the efficiency of repeated adjustment near either end of the array,
-     * e.g. repeated prepend or append operations. The effect on this PyByteArray is that:
+     * e.g. repeated prepend or append operations. The effect on this <code>PyByteArray</code> is
+     * that:
      *
      * <pre>
      * this.offset = f'
@@ -2357,7 +2366,8 @@
      * where the regions of length <code>a</code> and <code>b=size-(a+d)</code> have been preserved
      * and the gap between them adjusted to specification. The new offset f' is chosen heuristically
      * by the method to optimise the efficiency of repeated adjustment near either end of the array,
-     * e.g. repeated prepend or append operations. The effect on this PyByteArray is that:
+     * e.g. repeated prepend or append operations. The effect on this <code>PyByteArray</code> is
+     * that:
      *
      * <pre>
      * this.offset = f'
@@ -2439,7 +2449,7 @@
      *
      * where the contents of region <code>a</code> have been preserved, although possbly moved, and
      * the gap at the end has the requested size. this method never shrinks the total storage. The
-     * effect on this PyByteArray is that:
+     * effect on this <code>PyByteArray</code> is that:
      *
      * <pre>
      * this.offset = f or 0
@@ -2518,7 +2528,7 @@
      * </pre>
      *
      * where the regions of length <code>a</code> and <code>b=size-(a+d)</code> have been preserved
-     * and the gap between them eliminated. The effect on this PyByteArray is that:
+     * and the gap between them eliminated. The effect on this <code>PyByteArray</code> is that:
      *
      * <pre>
      * this.offset = f'
@@ -2526,8 +2536,8 @@
      * </pre>
      *
      * The method does not implement the Python repertoire of slice indices but avoids indexing
-     * outside the bytearray by silently adjusting a to be within it. Negative d is treated as 0 and
-     * if d is too large, it is truncated to the array end.
+     * outside the <code>bytearray</code> by silently adjusting a to be within it. Negative d is
+     * treated as 0 and if d is too large, it is truncated to the array end.
      *
      * @param a index of hole in byte array
      * @param d number to discard (will discard x[a,a+d-1])
@@ -2536,8 +2546,7 @@
     private void storageDelete(int a, int d) {
         // storageReplace specialised for delete (e=0)
 
-        if (d == 0)
-         {
+        if (d == 0) {
             return; // Everything stays where it is.
         }
 
@@ -2626,7 +2635,7 @@
      * where the regions of length <code>a</code> and <code>b=size-(a+e)</code> have been preserved
      * and the <code>e</code> intervening elements reduced to <code>e-d</code> elements, by removing
      * exactly the elements with indices (relative to the start of valid data) <code>a+k*c</code>
-     * for <code>k=0...d-1</code>. The effect on this PyByteArray is that:
+     * for <code>k=0...d-1</code>. The effect on this <code>PyByteArray</code> is that:
      *
      * <pre>
      * this.offset = f'
@@ -2634,8 +2643,8 @@
      * </pre>
      *
      * The method does not implement the Python repertoire of slice indices but avoids indexing
-     * outside the bytearray by silently adjusting a to be within it. Negative d is treated as 0 and
-     * if d is too large, it is truncated to the array end.
+     * outside the <code>bytearray</code> by silently adjusting a to be within it. Negative d is
+     * treated as 0 and if d is too large, it is truncated to the array end.
      *
      * @param a index of hole in byte array
      * @param c (>0) step size between the locations of elements to delete
@@ -2647,4 +2656,3 @@
         // XXX Change SequenceIndexDelegate to use (and PyList to implement) delslice()
     }
 }
-
diff --git a/src/org/python/core/PyDictionary.java b/src/org/python/core/PyDictionary.java
--- a/src/org/python/core/PyDictionary.java
+++ b/src/org/python/core/PyDictionary.java
@@ -32,10 +32,10 @@
 
     public static final PyType TYPE = PyType.fromClass(PyDictionary.class);
 
-    private final ConcurrentMap<PyObject, PyObject> map;
+    private final ConcurrentMap<PyObject, PyObject> internalMap;
 
     public ConcurrentMap<PyObject, PyObject> getMap() {
-        return map;
+        return internalMap;
     }
 
     /**
@@ -50,8 +50,8 @@
      */
     public PyDictionary(PyType type, int capacity) {
         super(type);
-        map = new ConcurrentHashMap<PyObject, PyObject>(capacity, Generic.CHM_LOAD_FACTOR,
-                                                          Generic.CHM_CONCURRENCY_LEVEL);
+        internalMap = new ConcurrentHashMap<PyObject,PyObject>(capacity, Generic.CHM_LOAD_FACTOR,
+                                                               Generic.CHM_CONCURRENCY_LEVEL);
     }
 
     /**
@@ -59,7 +59,7 @@
      */
     public PyDictionary(PyType type) {
         super(type);
-        map = Generic.concurrentMap();
+        internalMap = Generic.concurrentMap();
     }
 
     /**
@@ -75,7 +75,7 @@
     public PyDictionary(PyType type, Map<PyObject, PyObject> map) {
         this(type, Math.max((int) (map.size() / Generic.CHM_LOAD_FACTOR) + 1,
                             Generic.CHM_INITIAL_CAPACITY));
-        this.map.putAll(map);
+        getMap().putAll(map);
     }
 
     /**
@@ -86,9 +86,9 @@
     protected PyDictionary(PyType type, boolean initializeBacking) {
         super(type);
         if (initializeBacking) {
-            map = Generic.concurrentMap();
+            internalMap = Generic.concurrentMap();
         } else {
-            map = null; // for later initialization
+            internalMap = null; // for later initialization
         }
     }
 
@@ -101,6 +101,7 @@
      */
     public PyDictionary(PyObject elements[]) {
         this();
+        ConcurrentMap<PyObject, PyObject> map = getMap();
         for (int i = 0; i < elements.length; i += 2) {
             map.put(elements[i], elements[i + 1]);
         }
@@ -600,7 +601,7 @@
 
     @ExposedMethod(defaults = "null", doc = BuiltinDocs.dict_pop_doc)
     final PyObject dict_pop(PyObject key, PyObject defaultValue) {
-        if (!map.containsKey(key)) {
+        if (!getMap().containsKey(key)) {
             if (defaultValue == null) {
                 throw Py.KeyError("popitem(): dictionary is empty");
             }
@@ -738,7 +739,9 @@
             return false;
         }
         final PyDictionary other = (PyDictionary) obj;
-        if (this.map != other.map && (this.map == null || !this.map.equals(other.map))) {
+        ConcurrentMap<PyObject, PyObject> map = getMap();
+        ConcurrentMap<PyObject, PyObject> otherMap = other.getMap();
+        if (map != otherMap && (map == null || !map.equals(otherMap))) {
             return false;
         }
         return true;
diff --git a/src/org/python/core/PyString.java b/src/org/python/core/PyString.java
--- a/src/org/python/core/PyString.java
+++ b/src/org/python/core/PyString.java
@@ -2192,10 +2192,7 @@
 
     @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.str_translate_doc)
     final String str_translate(String table, String deletechars) {
-        if (table == null) {
-            return getString();
-        }
-        if (table.length() != 256)
+        if (table != null && table.length() != 256)
             throw Py.ValueError(
                 "translation table must be 256 characters long");
 
@@ -2204,12 +2201,16 @@
             char c = getString().charAt(i);
             if (deletechars != null && deletechars.indexOf(c) >= 0)
                 continue;
-            try {
-                buf.append(table.charAt(c));
-            }
-            catch (IndexOutOfBoundsException e) {
-                throw Py.TypeError(
-                    "translate() only works for 8-bit character strings");
+            if(table == null) {
+                buf.append(c);
+            } else {
+                try {
+                    buf.append(table.charAt(c));
+                }
+                catch (IndexOutOfBoundsException e) {
+                    throw Py.TypeError(
+                        "translate() only works for 8-bit character strings");
+                }
             }
         }
         return buf.toString();
diff --git a/src/org/python/core/PySystemState.java b/src/org/python/core/PySystemState.java
--- a/src/org/python/core/PySystemState.java
+++ b/src/org/python/core/PySystemState.java
@@ -59,6 +59,7 @@
     private static final String JAR_URL_PREFIX = "jar:file:";
     private static final String JAR_SEPARATOR = "!";
     private static final String VFSZIP_PREFIX = "vfszip:";
+    private static final String VFS_PREFIX = "vfs:";
 
     public static final PyString version = new PyString(Version.getVersion());
 
@@ -1183,6 +1184,19 @@
                         }
                         jarFileName = urlString.substring(start, jarIndex);
                     }
+                } else if (urlString.startsWith(VFS_PREFIX)) {
+                    // vfs:/some/path/jython.jar/org/python/core/PySystemState.class
+                    final String path = PySystemState.class.getName().replace('.', '/');
+                    int jarIndex = urlString.indexOf(".jar/".concat(path));
+                    if (jarIndex > 0) {
+                        jarIndex += 4;
+                        int start = VFS_PREFIX.length();
+                        if (Platform.IS_WINDOWS) {
+                            // vfs:/C:/some/path/jython.jar/org/python/core/PySystemState.class
+                            start++;
+                        }
+                        jarFileName = urlString.substring(start, jarIndex);
+                    }
                 }
             } catch (Exception e) {}
         }
diff --git a/src/org/python/core/PyXRange.java b/src/org/python/core/PyXRange.java
--- a/src/org/python/core/PyXRange.java
+++ b/src/org/python/core/PyXRange.java
@@ -127,8 +127,7 @@
 
     @Override
     protected PyObject getslice(int start, int stop, int step) {
-        // not supported
-        return null;
+        throw Py.TypeError("xrange index must be integer, not 'slice'");
     }
 
     @Override
diff --git a/src/org/python/core/buffer/BaseBuffer.java b/src/org/python/core/buffer/BaseBuffer.java
--- a/src/org/python/core/buffer/BaseBuffer.java
+++ b/src/org/python/core/buffer/BaseBuffer.java
@@ -22,7 +22,7 @@
  * passed to the constructor. Otherwise, all methods for write access raise a
  * <code>BufferError</code> read-only exception and {@link #isReadonly()} returns <code>true</code>.
  * Sub-classes can follow the same pattern, setting {@link PyBUF#WRITABLE} in the constructor and,
- * if they have to override the operations that write (<code>storeAt</code> and
+ * if they have to, overriding the operations that write (<code>storeAt</code> and
  * <code>copyFrom</code>). The recommended pattern is:
  *
  * <pre>
@@ -31,9 +31,10 @@
  * }
  * // ... implementation of the write operation
  * </pre>
- *
- * The implementors of simple buffers will find it efficient to override the generic access methods
- * to which performance might be sensitive, with a calculation specific to their actual type.
+ * Another approach, used in the standard library, is to have distinct classes for the writable and
+ * read-only variants. The implementors of simple buffers will find it efficient to override the
+ * generic access methods to which performance might be sensitive, with a calculation specific to
+ * their actual type.
  * <p>
  * At the time of writing, only one-dimensional buffers of item size one are used in the Jython
  * core.
diff --git a/src/org/python/core/buffer/SimpleBuffer.java b/src/org/python/core/buffer/SimpleBuffer.java
--- a/src/org/python/core/buffer/SimpleBuffer.java
+++ b/src/org/python/core/buffer/SimpleBuffer.java
@@ -48,6 +48,7 @@
      * @throws ArrayIndexOutOfBoundsException if <code>index0</code> and <code>size</code> are
      *             inconsistent with <code>storage.length</code>
      */
+    // XXX: "for sub-class use" = should be protected?
     public SimpleBuffer(byte[] storage, int index0, int size) throws PyException,
             ArrayIndexOutOfBoundsException {
         this();
@@ -90,6 +91,7 @@
      * @param storage the array of bytes storing the implementation of the exporting object
      * @throws NullPointerException if <code>storage</code> is null
      */
+    // XXX: "for sub-class use" = should be protected?
     public SimpleBuffer(byte[] storage) throws NullPointerException {
         this();
         this.storage = storage;         // Exported data (index0=0 from initialisation)
diff --git a/src/org/python/core/buffer/SimpleStringBuffer.java b/src/org/python/core/buffer/SimpleStringBuffer.java
--- a/src/org/python/core/buffer/SimpleStringBuffer.java
+++ b/src/org/python/core/buffer/SimpleStringBuffer.java
@@ -6,11 +6,11 @@
 /**
  * Buffer API that appears to be a one-dimensional array of one-byte items providing read-only API,
  * but which is actually backed by a Java String. Some of the buffer API absolutely needs access to
- * the data as a byte array (those parts that involve a {@link PyBuffer.Pointer} result), and therefore
- * this class must create a byte array from the String for them. However, it defers creation of a
- * byte array until that part of the API is actually used. Where possible, this class overrides
- * those methods in SimpleBuffer that would otherwise access the byte array attribute to use the
- * String instead.
+ * the data as a byte array (those parts that involve a <code>PyBuffer.Pointer</code> result), and
+ * therefore this class must create a byte array from the String for them. However, it defers
+ * creation of a byte array until that part of the API is actually used. Where possible, this class
+ * overrides those methods in SimpleBuffer that would otherwise access the byte array attribute to
+ * use the String instead.
  */
 public class SimpleStringBuffer extends SimpleBuffer {
 
diff --git a/src/org/python/core/buffer/Strided1DBuffer.java b/src/org/python/core/buffer/Strided1DBuffer.java
--- a/src/org/python/core/buffer/Strided1DBuffer.java
+++ b/src/org/python/core/buffer/Strided1DBuffer.java
@@ -9,21 +9,24 @@
  * properties in the usual way, designating a slice (or all) of a byte array, but also a
  * <code>stride</code> property (equal to <code>getStrides()[0]</code>).
  * <p>
- * Let this underlying buffer be the byte array <i>u(i)</i> for <i>i=0..N-1</i>, let <i>x</i> be the
+ * Let the underlying buffer be the byte array <i>u(i)</i> for <i>i=0..N-1</i>, let <i>x</i> be the
  * <code>Strided1DBuffer</code>, and let the stride be <i>p</i>. The storage works as follows.
  * Designate by <i>x(j)</i>, for <i>j=0..L-1</i>, the byte at index <i>j</i>, that is, the byte
- * retrieved by <code>x.byteAt(j)</code>. Then, we store <i>x(j)</i> at <i>u(a+pj)</i>, that is,
- * <i>x(0)</i> is at <i>u(a)</i>. When we construct such a buffer, we have to supply <i>a</i> =
+ * retrieved by <code>x.byteAt(j)</code>. Thus, we store <i>x(j)</i> at <i>u(a+pj)</i>, that is,
+ * <i>x(0) = u(a)</i>. When we construct such a buffer, we have to supply <i>a</i> =
  * <code>index0</code>, <i>L</i> = <code>length</code>, and <i>p</i> = <code>stride</code> as the
  * constructor arguments. The last item in the slice <i>x(L-1)</i> is stored at <i>u(a+p(L-1))</i>.
- * If <i>p<0</i> and <i>L>1</i>, this will be to the left of <i>u(a)</i>, so the constructor
- * argument index0 is not then the low index of the range occupied by the data. Clearly both these
- * indexes must be in the range 0 to <i>N-1</i> inclusive, a rule enforced by the constructors
+ * For the simple case of positive stride, constructor argument <code>index0</code> is the low index
+ * of the range occupied by the data. When the stride is negative, that is to say <i>p<0</i>, and
+ * <i>L>1</i>, this will be to the left of <i>u(a)</i>, and the constructor argument
+ * <code>index0</code> is not then the low index of the range occupied by the data. Clearly both
+ * these indexes must be in the range 0 to <i>N-1</i> inclusive, a rule enforced by the constructors
  * (unless <i>L=0</i>, when it is assumed no array access will take place).
  * <p>
  * The class may be used by exporters to create a strided slice (e.g. to export the diagonal of a
  * matrix) and in particular by other buffers to create strided slices of themselves, such as to
- * create the memoryview that is returned as an extended slice of a memoryview.
+ * create the <code>memoryview</code> that is returned as an extended slice of a
+ * <code>memoryview</code>.
  */
 public class Strided1DBuffer extends BaseBuffer {
 
@@ -60,9 +63,14 @@
 
     /**
      * Provide an instance of <code>Strided1DBuffer</code> with navigation variables initialised,
-     * for sub-class use. The buffer ( {@link #storage}, {@link #index0}), and the navigation (
+     * for sub-class use. The buffer ({@link #storage}, {@link #index0}), and the navigation (
      * {@link #shape} array and {@link #stride}) will be initialised from the arguments (which are
      * checked for range).
+     * <p>
+     * The sub-class constructor should check that the intended access is compatible with this
+     * object by calling {@link #checkRequestFlags(int)}. (See the source of
+     * {@link Strided1DWritableBuffer#Strided1DWritableBuffer(int, byte[], int, int, int)}
+     * for an example of this use.)
      *
      * @param storage raw byte array containing exported data
      * @param index0 index into storage of item[0]
@@ -72,6 +80,7 @@
      * @throws ArrayIndexOutOfBoundsException if <code>index0</code>, <code>length</code> and
      *             <code>stride</code> are inconsistent with <code>storage.length</code>
      */
+    // XXX: "for sub-class use" = should be protected?
     public Strided1DBuffer(byte[] storage, int index0, int length, int stride)
             throws ArrayIndexOutOfBoundsException, NullPointerException {
         this();
diff --git a/src/org/python/core/buffer/Strided1DWritableBuffer.java b/src/org/python/core/buffer/Strided1DWritableBuffer.java
--- a/src/org/python/core/buffer/Strided1DWritableBuffer.java
+++ b/src/org/python/core/buffer/Strided1DWritableBuffer.java
@@ -88,7 +88,7 @@
 
         if (length > 0) {
             // Translate start relative to underlying buffer
-            int compStride= this.stride * stride;
+            int compStride = this.stride * stride;
             int compIndex0 = index0 + start * this.stride;
             // Construct a view, taking a lock on the root object (this or this.root)
             return new SlicedView(getRoot(), flags, storage, compIndex0, length, compStride);
diff --git a/src/org/python/core/codecs.java b/src/org/python/core/codecs.java
--- a/src/org/python/core/codecs.java
+++ b/src/org/python/core/codecs.java
@@ -919,11 +919,11 @@
      * This method differs from the CPython equivalent (in <code>Object/unicodeobject.c</code>)
      * which works with an array of code points that are, in a wide build, Unicode code points.
      *
-     * @param unicode
-     * @param base64SetO
-     * @param base64WhiteSpace
-     * @param errors
-     * @return
+     * @param unicode to be encoded
+     * @param base64SetO true if characters in "set O" should be translated to base64
+     * @param base64WhiteSpace true if white-space characters should be translated to base64
+     * @param errors error policy name (e.g. "ignore", "replace")
+     * @return bytes representing the encoded unicode string
      */
     public static String PyUnicode_EncodeUTF7(String unicode, boolean base64SetO,
             boolean base64WhiteSpace, String errors) {
diff --git a/src/org/python/core/io/StreamIO.java b/src/org/python/core/io/StreamIO.java
--- a/src/org/python/core/io/StreamIO.java
+++ b/src/org/python/core/io/StreamIO.java
@@ -276,63 +276,7 @@
     }
 
     private static ReadableByteChannel newChannel(InputStream in) {
-        return new InternalReadableByteChannel(in);
-    }
-
-
-    /*
-     * AbstractInterruptibleChannel is used for its end() and begin() implementations
-     * but this Channel is not really interruptible.
-     */
-    private static class InternalReadableByteChannel
-        extends AbstractInterruptibleChannel
-        implements ReadableByteChannel {
-
-        private InputStream in;
-        private boolean open = true;
-
-        InternalReadableByteChannel(InputStream in) {
-            this.in = in;
-        }
-
-        public int read(ByteBuffer dst) throws IOException {
-            final int CHUNK = 8192;
-
-            int len = dst.remaining();
-            int totalRead = 0;
-            int bytesRead = 0;
-
-            byte buf[] = new byte[0];
-            while (totalRead < len) {
-                int bytesToRead = Math.min((len - totalRead), CHUNK);
-                if (buf.length < bytesToRead) {
-                    buf = new byte[bytesToRead];
-                }
-                try {
-                    begin();
-                    bytesRead = in.read(buf, 0, bytesToRead);
-                } finally {
-                    end(bytesRead > 0);
-                }
-                if (bytesRead < 0) {
-                    break;
-                } else {
-                    totalRead += bytesRead;
-                }
-                dst.put(buf, 0, bytesRead);
-            }
-            if ((bytesRead < 0) && (totalRead == 0)) {
-                return -1;
-            }
-            return totalRead;
-        }
-
-        protected void implCloseChannel() throws IOException {
-            if (open) {
-                in.close();
-                open = false;
-            }
-        }
+        return Channels.newChannel(in);
     }
 
 }
diff --git a/src/org/python/modules/SHA224Digest.java b/src/org/python/modules/SHA224Digest.java
--- a/src/org/python/modules/SHA224Digest.java
+++ b/src/org/python/modules/SHA224Digest.java
@@ -1,6 +1,6 @@
 package org.python.modules;
 
-/**
+/*
  *  Copyright 2011 Gaurav Raje
  *  Licensed to PSF under a Contributor Agreement.
  */
@@ -8,13 +8,14 @@
 
 /**
  * SHA-224 as described in RFC 3874. This introduces the SHA224 Digest which has
- * been ommitted from the JDK {@link java.security}.
- * 
+ * been omitted from the JDK <code>java.security</code>.
  * 
  * This implementation has been borrowed from the Bouncy Castle implementation
- * of SHA2 algorithms. Since they are MIT Licensed, they are compatible with
+ * of SHA2 algorithms.
+ * 
+ * Since they are MIT Licensed, they are compatible with
  * this project. Their mandatory copyright notice follows.
- * 
+ * <pre>
  * Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle
  * (http://www.bouncycastle.org)
  * 
@@ -35,6 +36,7 @@
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
+ * </pre>
  */
 public class SHA224Digest
         extends MessageDigest
diff --git a/src/org/python/modules/_codecs.java b/src/org/python/modules/_codecs.java
--- a/src/org/python/modules/_codecs.java
+++ b/src/org/python/modules/_codecs.java
@@ -314,12 +314,12 @@
      * @param mapping to convert bytes to characters
      * @return decoded string and number of bytes consumed
      */
-    public static PyTuple charmap_decode(String str, String errors, PyObject mapping) {
+    public static PyTuple charmap_decode(String bytes, String errors, PyObject mapping) {
         if (mapping == null || mapping == Py.None) {
             // Default to Latin-1
-            return latin_1_decode(str, errors);
+            return latin_1_decode(bytes, errors);
         } else {
-            return charmap_decode(str, errors, mapping, false);
+            return charmap_decode(bytes, errors, mapping, false);
         }
     }
 
@@ -1290,7 +1290,6 @@
      * @param bytes to be decoded (Jython {@link PyString} convention)
      * @param errors error policy name (e.g. "ignore", "replace")
      * @param byteorder decoding "endianness" specified (in the Python -1, 0, +1 convention)
-     * @param isFinal if a "final" call, meaning the input must all be consumed
      * @return tuple (unicode_result, bytes_consumed, endianness)
      */
     public static PyTuple utf_32_ex_decode(String bytes, String errors, int byteorder) {
@@ -1335,7 +1334,6 @@
      * @param order LE, BE or UNDEFINED (meaning bytes may begin with a byte order mark)
      * @param isFinal if a "final" call, meaning the input must all be consumed
      * @param findOrder if the returned tuple should include a report of the byte order
-     * @return tuple containing unicode result (as UTF-16 Java String)
      * @return tuple (unicode_result, bytes_consumed [, endianness])
      */
     private static PyTuple PyUnicode_DecodeUTF32Stateful(String bytes, String errors,
diff --git a/src/org/python/modules/_csv/PyReader.java b/src/org/python/modules/_csv/PyReader.java
--- a/src/org/python/modules/_csv/PyReader.java
+++ b/src/org/python/modules/_csv/PyReader.java
@@ -69,11 +69,15 @@
             lineobj = input_iter.__iternext__();
             if (lineobj == null) {
                 // End of input OR exception
-                if (field.length() != 0) {
-                    throw _csv.Error("newline inside string");
-                } else {
-                    return null;
+                if (field.length() != 0 || state == ParserState.IN_QUOTED_FIELD) {
+                    if (dialect.strict) {
+                        throw _csv.Error("unexpected end of data");
+                    } else {
+                        parse_save_field();
+                        break;
+                    }
                 }
+                return null;
             }
 
             line_num++;
diff --git a/src/org/python/modules/_csv/PyWriter.java b/src/org/python/modules/_csv/PyWriter.java
--- a/src/org/python/modules/_csv/PyWriter.java
+++ b/src/org/python/modules/_csv/PyWriter.java
@@ -3,6 +3,7 @@
 
 import org.python.core.Py;
 import org.python.core.PyException;
+import org.python.core.PyFloat;
 import org.python.core.PyObject;
 import org.python.core.PyString;
 import org.python.core.PyType;
@@ -130,7 +131,16 @@
             } else if (field == Py.None) {
                 append_ok = join_append("", len == 1);
             } else {
-                PyObject str = field.__str__();
+            	
+                PyObject str;
+                //XXX: in 3.x this check can go away and we can just always use
+                //     __str__
+                if (field.getClass() == PyFloat.class) {
+                	str = field.__repr__();
+                } else {
+                	str = field.__str__();
+                }
+
                 if (str == null) {
                     return false;
                 }
diff --git a/src/org/python/modules/_io/OpenMode.java b/src/org/python/modules/_io/OpenMode.java
--- a/src/org/python/modules/_io/OpenMode.java
+++ b/src/org/python/modules/_io/OpenMode.java
@@ -43,8 +43,7 @@
     /**
      * Error message describing the way in which the mode is invalid, or null if no problem has been
      * found. This field may be set by the constructor (in the case of duplicate or unrecognised
-     * mode letters), by the {@link #isValid()} method, or by client code. A non-null value will
-     * cause {@link #isValid()} to return false.
+     * mode letters), by the {@link #validate()} method, or by client code.
      */
     public String message;
 
@@ -52,7 +51,7 @@
      * Decode the given string to an OpenMode object, checking for duplicate or unrecognised mode
      * letters. Valid letters are those in "rwa+btU". Errors in the mode string do not raise an
      * exception, they simply generate an appropriate error message in {@link #message}. After
-     * construction, a client should always call {@link #isValid()} to complete validity checks.
+     * construction, a client should always call {@link #validate()} to complete validity checks.
      *
      * @param mode
      */
@@ -196,7 +195,7 @@
      */
     public void checkValid() throws PyException {
 
-        // Actually peform the check
+        // Actually perform the check
         validate();
 
         // The 'other' flag reports alien symbols in the original mode string
@@ -215,7 +214,7 @@
     /**
      * The mode string we need when constructing a <code>FileIO</code> initialised with the present
      * mode. Note that this is not the same as the full open mode because it omits the text-based
-     * attributes, and not the same as {@link #raw()}.
+     * attributes.
      *
      * @return "r", "w", or "a" with optional "+".
      */
diff --git a/src/org/python/modules/_io/PyFileIO.java b/src/org/python/modules/_io/PyFileIO.java
--- a/src/org/python/modules/_io/PyFileIO.java
+++ b/src/org/python/modules/_io/PyFileIO.java
@@ -1,15 +1,10 @@
-/* Copyright (c)2012 Jython Developers */
+/* Copyright (c)2013 Jython Developers */
 package org.python.modules._io;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
-import java.nio.channels.Channel;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
 
 import org.python.core.ArgParser;
 import org.python.core.BuiltinDocs;
@@ -26,6 +21,7 @@
 import org.python.core.PyUnicode;
 import org.python.core.io.FileIO;
 import org.python.core.io.RawIOBase;
+import org.python.core.io.StreamIO;
 import org.python.expose.ExposedGet;
 import org.python.expose.ExposedMethod;
 import org.python.expose.ExposedNew;
@@ -39,8 +35,8 @@
 
     public static final PyType TYPE = PyType.fromClass(PyFileIO.class);
 
-    /** The FileIO to which we delegate operations not complete locally. */
-    private FileIO ioDelegate;
+    /** The {@link FileIO} or {@link StreamIO} to which we delegate operations not complete locally. */
+    private RawIOBase ioDelegate;
 
     /*
      * Implementation note: CPython fileio does not use the base-class, possibly overridden,
@@ -105,12 +101,15 @@
      */
     public PyFileIO(PyType subtype, PyObject file, OpenMode mode, boolean closefd) {
         super(subtype);
-        setDelegate(file, mode, closefd);
-        this.closefd = closefd;
 
+        // Establish the direction(s) of flow
         readable = mode.reading | mode.updating;
         writable = mode.writing | mode.updating | mode.appending;
 
+        // Assign a delegate according to the file argument
+        this.closefd = closefd;
+        setDelegate(file, mode);
+
         // The mode string of a raw file always asserts it is binary: "rb", "rb+", or "wb".
         if (readable) {
             this.mode = new PyString(writable ? "rb+" : "rb");
@@ -132,9 +131,8 @@
      *
      * @param file name or descriptor
      * @param mode parsed file open mode
-     * @param closefd must be true if file is in fact a name (checked, not used)
      */
-    private void setDelegate(PyObject file, OpenMode mode, boolean closefd) {
+    private void setDelegate(PyObject file, OpenMode mode) {
 
         if (file instanceof PyString) {
             // Open a file by name
@@ -151,23 +149,13 @@
              */
             Object fd = file.__tojava__(Object.class);
 
-            if (fd instanceof RawIOBase) {
-                // It is the "Jython file descriptor" from which we can get a channel.
+            if (fd instanceof FileIO || fd instanceof StreamIO) {
                 /*
-                 * If the argument were a FileIO, could we assign it directly to ioDelegate? I think
-                 * not: there would be a problem with close and closefd=False since it is not the
-                 * PyFileIO that keeps state.
+                 * It is the "Jython file descriptor", of a type suitable to be the ioDelegate. The
+                 * allowed types are able to give us a non-null InputStream or OutputStream,
+                 * according to direction.
                  */
-                Channel channel = ((RawIOBase)fd).getChannel();
-                if (channel instanceof FileChannel) {
-                    if (channel.isOpen()) {
-                        FileChannel fc = (FileChannel)channel;
-                        ioDelegate = new FileIO(fc, mode.forFileIO());
-                    } else {
-                        // File not open (we have to check as FileIO doesn't)
-                        throw Py.OSError(Errno.EBADF);
-                    }
-                }
+                ioDelegate = (RawIOBase)fd;
             }
         }
 
@@ -175,11 +163,22 @@
         if (ioDelegate == null) {
             // The file was a type we don't know how to use
             throw Py.TypeError(String.format("invalid file: %s", file.__repr__().asString()));
+
+        } else {
+
+            if (ioDelegate.closed()) {
+                // A closed file descriptor is a "bad descriptor"
+                throw Py.OSError(Errno.EBADF);
+            }
+
+            if ((readable && !ioDelegate.readable()) || (writable && !ioDelegate.writable())) {
+                // Requested mode in conflict with underlying file or stream
+                throw tailoredValueError(readable ? "read" : "writ");
+            }
+
+            // The name is either the textual name or a file descriptor (see Python docs)
+            fastGetDict().__setitem__("name", file);
         }
-
-        // The name is either the textual name or a file descriptor (see Python docs)
-        fastGetDict().__setitem__("name", file);
-
     }
 
     private static final String[] openArgs = {"file", "mode", "closefd"};
@@ -239,8 +238,8 @@
             PyArray a = (PyArray)buf;
 
             try {
-                ReadableByteChannel ch = ioDelegate.getChannel();
-                InputStream is = Channels.newInputStream(ch);
+                // The ioDelegate, if readable, can always provide an InputStream (see setDelegate)
+                InputStream is = ioDelegate.asInputStream();
                 count = a.fillFromStream(is);
                 count *= a.getItemsize();
             } catch (IOException ioe) {
@@ -281,8 +280,8 @@
         if (buf instanceof PyArray) {
             // Special case: PyArray knows how to write itself
             try {
-                WritableByteChannel ch = ioDelegate.getChannel();
-                OutputStream os = Channels.newOutputStream(ch);
+                // The ioDelegate, if writable, can always provide an OutputStream (see setDelegate)
+                OutputStream os = ioDelegate.asOutputStream();
                 count = ((PyArray)buf).toStream(os);
             } catch (IOException ioe) {
                 throw Py.IOError(ioe);
diff --git a/src/org/python/modules/_io/PyIOBase.java b/src/org/python/modules/_io/PyIOBase.java
--- a/src/org/python/modules/_io/PyIOBase.java
+++ b/src/org/python/modules/_io/PyIOBase.java
@@ -160,7 +160,6 @@
      * Truncate file to <code>size</code> bytes to the current position (as reported by
      * <code>tell()</code>).
      *
-     * @param size requested for stream (or null for default)
      * @return the new size
      */
     public long truncate() {
@@ -182,7 +181,10 @@
     }
 
     @ExposedMethod(doc = flush_doc)
-    final void _IOBase_flush() {}
+    final void _IOBase_flush() {
+        // Even types for which this remains a no-op must complain if closed (e.g. BytesIO)
+        _checkClosed();
+    }
 
     /**
      * True if the object is closed to further <b>client</b> operations. It is the state accessed by
@@ -592,7 +594,7 @@
              */
             PyByteArray res = new PyByteArray();
 
-            while (remainingLimit > 0) {
+            while (--remainingLimit >= 0) {
 
                 /*
                  * read() returns a str of one byte, doing at most one read to refill, or it returns
diff --git a/src/org/python/modules/_io/PyRawIOBase.java b/src/org/python/modules/_io/PyRawIOBase.java
--- a/src/org/python/modules/_io/PyRawIOBase.java
+++ b/src/org/python/modules/_io/PyRawIOBase.java
@@ -187,7 +187,7 @@
      * of bytes written.
      *
      * @param b buffer of bytes to be written
-     * @return
+     * @return the number of bytes written
      */
     public PyObject write(PyObject b) {
         return _RawIOBase_write(b);
diff --git a/src/org/python/modules/_sre.java b/src/org/python/modules/_sre.java
--- a/src/org/python/modules/_sre.java
+++ b/src/org/python/modules/_sre.java
@@ -22,6 +22,9 @@
 public class _sre {
     public static int MAGIC = SRE_STATE.SRE_MAGIC;
 
+    // probably the right number for Jython since we are UTF-16.
+    public static int MAXREPEAT = 65535;
+ 
     // workaround the fact that H, I types are unsigned, but we are not really using them as such
     // XXX: May not be the right size, but I suspect it is -- see sre_compile.py
     public static int CODESIZE = 4;
diff --git a/src/org/python/modules/_threading/Condition.java b/src/org/python/modules/_threading/Condition.java
--- a/src/org/python/modules/_threading/Condition.java
+++ b/src/org/python/modules/_threading/Condition.java
@@ -38,12 +38,16 @@
     }
 
     public boolean acquire() {
-        return Condition_acquire();
+        return Condition_acquire(true);
     }
 
-    @ExposedMethod
-    final boolean Condition_acquire() {
-        return _lock.acquire();
+    public boolean acquire(boolean blocking) {
+        return Condition_acquire(blocking);
+    }
+
+    @ExposedMethod(defaults = "true")
+    final boolean Condition_acquire(boolean blocking) {
+        return _lock.acquire(blocking);
     }
 
     public PyObject __enter__(ThreadState ts) {
@@ -53,7 +57,7 @@
 
     @ExposedMethod
     final PyObject Condition___enter__() {
-        Condition_acquire();
+        Condition_acquire(true);
         return this;
     }
 
diff --git a/src/org/python/modules/time/Time.java b/src/org/python/modules/time/Time.java
--- a/src/org/python/modules/time/Time.java
+++ b/src/org/python/modules/time/Time.java
@@ -780,6 +780,11 @@
                     builder.append("'");
                     inQuote = needsQuote;
                 }
+                if (charAt == '\'') {
+                    // a single quote always needs to be escaped, regardless
+                    // whether already in a quote or not
+                    builder.append("'");
+                }
                 builder.append(charAt);
                 continue;
             } else if (inQuote) {
diff --git a/src/org/python/util/ProxyCompiler.java b/src/org/python/util/ProxyCompiler.java
--- a/src/org/python/util/ProxyCompiler.java
+++ b/src/org/python/util/ProxyCompiler.java
@@ -5,21 +5,22 @@
 import org.python.core.PySystemState;
 
 public class ProxyCompiler {
+
     /**
      * Compiles the python file by loading it
-     * 
-     * FIXME: this is quite hackish right now. It basically starts a whole interpreter
-     * and set's proxyDebugDirectory as the destination for the compiled proxy class 
-     * 
-     * @param filename: python filename to exec
-     * @param destDir: destination directory for the proxy classes
+     *
+     * FIXME: this is quite hackish right now. It basically starts a whole interpreter and sets
+     * proxyDebugDirectory as the destination for the compiled proxy class
+     *
+     * @param filename python filename to exec
+     * @param destDir destination directory for the proxy classes
      */
     public static void compile(String filename, String destDir) {
         Properties props = new Properties(System.getProperties());
         props.setProperty(PySystemState.PYTHON_CACHEDIR_SKIP, "true");
         PySystemState.initialize(props, null);
         PythonInterpreter interp = new PythonInterpreter();
-        
+
         String origProxyDir = org.python.core.Options.proxyDebugDirectory;
         try {
             org.python.core.Options.proxyDebugDirectory = destDir;
diff --git a/tests/java/org/python/modules/_io/_ioTest.java b/tests/java/org/python/modules/_io/_ioTest.java
--- a/tests/java/org/python/modules/_io/_ioTest.java
+++ b/tests/java/org/python/modules/_io/_ioTest.java
@@ -5,9 +5,15 @@
 import static org.junit.matchers.JUnitMatchers.*;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
-import java.io.IOError;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.WritableByteChannel;
 import java.util.Arrays;
 
 import org.junit.Before;
@@ -18,6 +24,7 @@
 import org.python.core.PyObject;
 import org.python.core.PyStringMap;
 import org.python.core.PySystemState;
+import org.python.core.imp;
 import org.python.core.io.RawIOBase;
 import org.python.util.PythonInterpreter;
 
@@ -28,7 +35,12 @@
  */
 public class _ioTest {
 
-    /** We need the interpreter to be initialised for these tests **/
+    // Some file names to use
+    private final String FILE1 = "$test_1_tmp";
+    private final String FILE2 = "$test_2_tmp";
+    private final String FILE3 = "$test_3_tmp";
+
+    // We need the interpreter to be initialised for these tests.
     PySystemState systemState;
     PyStringMap dict;
     PythonInterpreter interp;
@@ -94,6 +106,61 @@
         }
     }
 
+    /** Check <code>PyFile().fileno()</code> is acceptable to <code>_io.open()</code> */
+    @Test
+    public void openPyFileByFileno() throws IOException {
+        PySystemState sys = Py.getSystemState();
+        PyFile file = new PyFile(FILE1, "w", 1);
+        openByFilenoTest(file, "wb");
+    }
+
+    /** Check <code>PyFile(OutputStream).fileno()</code> is acceptable to <code>_io.open()</code> */
+    @Test
+    public void openPyFileOStreamByFileno() throws IOException {
+        PySystemState sys = Py.getSystemState();
+        OutputStream ostream = new FileOutputStream(FILE1);
+        PyFile file = new PyFile(ostream);
+        openByFilenoTest(file, "wb");
+    }
+
+    /** Check <code>sys.stdin.fileno()</code> is acceptable to <code>_io.open()</code> */
+    @Test
+    public void openStdinByFileno() throws IOException {
+        PySystemState sys = Py.getSystemState();
+        openByFilenoTest(sys.stdin, "rb");
+    }
+
+    /** Check <code>sys.stdout.fileno()</code> is acceptable to <code>_io.open()</code> */
+    @Test
+    public void openStdoutByFileno() throws IOException {
+        PySystemState sys = Py.getSystemState();
+        openByFilenoTest(sys.stdout, "wb");
+    }
+
+    /** Check <code>sys.stderr.fileno()</code> is acceptable to <code>_io.open()</code> */
+    @Test
+    public void openStderrByFileno() throws IOException {
+        PySystemState sys = Py.getSystemState();
+        openByFilenoTest(sys.stderr, "wb");
+    }
+
+    /**
+     * Test opening by a "file descriptor" obtained from another file or stream. The main purpose is
+     * to test that the return from <code>fileno()</code> is acceptable to <code>_io.open()</code>.
+     *
+     * @param file anything with a "fileno" function
+     * @param mode mode string "rb" etc.
+     * @throws IOException
+     */
+    public void openByFilenoTest(PyObject file, String mode) throws IOException {
+        PyObject pyfd = file.invoke("fileno");
+        RawIOBase fd = (RawIOBase)pyfd.__tojava__(RawIOBase.class);
+        PyObject[] args = new PyObject[] {pyfd, Py.newString(mode), Py.False};
+        String[] kwds = {"closefd"};
+        PyObject file2 = _io.open(args, kwds);
+        file2.invoke("close");
+    }
+
     /**
      * Test automatic closing of files when the interpreter finally exits. Done correctly, text
      * written to any kind of file-like object should be flushed to disk and the file closed when
@@ -106,9 +173,9 @@
     public void closeNeglectedFiles() throws IOException {
 
         // File names
-        final String F = "$test_1_tmp";     // Raw file
-        final String FB = "$test_2_tmp";    // Buffered file
-        final String FT = "$test_3_tmp";    // Test file
+        final String F = FILE1;     // Raw file
+        final String FB = FILE2;    // Buffered file
+        final String FT = FILE3;    // Test file
 
         String expText = "Line 1\nLine 2\nLine 3.";     // Note: all ascii, but with new lines
         byte[] expBytes = expText.getBytes();
@@ -149,11 +216,11 @@
         assertTrue(pyft.__closed);
 
         // If they were not closed properly not all bytes will reach the files.
-        checkFileContent(F, expBytes);
-        checkFileContent(FB, expBytes);
+        checkFileContent(F, expBytes, true);
+        checkFileContent(FB, expBytes, true);
 
         // Expect that TextIOWrapper should have adjusted the line separator
-        checkFileContent(FT, newlineFix(expText));
+        checkFileContent(FT, newlineFix(expText), true);
     }
 
     /**
@@ -165,9 +232,10 @@
     @Test
     public void closeNeglectedPyFiles() throws IOException {
 
-        final String F = "$test_1_tmp";     // Raw file
-        final String FB = "$test_2_tmp";    // Buffered file
-        final String FT = "$test_3_tmp";    // Test file
+        // File names
+        final String F = FILE1;     // Raw file
+        final String FB = FILE2;    // Buffered file
+        final String FT = FILE3;    // Test file
 
         String expText = "Line 1\nLine 2\nLine 3.";
         byte[] expBytes = expText.getBytes();
@@ -183,19 +251,19 @@
         interp.exec("f = open('" + F + "', 'wb', 0)");
         PyFile pyf = (PyFile)interp.get("f");
         assertNotNull(pyf);
-        RawIOBase r = (RawIOBase) pyf.fileno().__tojava__(RawIOBase.class);
+        RawIOBase r = (RawIOBase)pyf.fileno().__tojava__(RawIOBase.class);
 
         // This should get us a buffered binary PyFile called fb
         interp.exec("fb = open('" + FB + "', 'wb')");
         PyFile pyfb = (PyFile)interp.get("fb");
         assertNotNull(pyfb);
-        RawIOBase rb = (RawIOBase) pyfb.fileno().__tojava__(RawIOBase.class);
+        RawIOBase rb = (RawIOBase)pyfb.fileno().__tojava__(RawIOBase.class);
 
         // This should get us an buffered text PyFile called ft
         interp.exec("ft = open('" + FT + "', 'w')");
         PyFile pyft = (PyFile)interp.get("ft");
         assertNotNull(pyft);
-        RawIOBase rt = (RawIOBase) pyft.fileno().__tojava__(RawIOBase.class);
+        RawIOBase rt = (RawIOBase)pyft.fileno().__tojava__(RawIOBase.class);
 
         // Write the bytes test material to each file but don't close it
         interp.exec("f.write(b)");
@@ -211,39 +279,47 @@
         assertTrue(rt.closed());
 
         // If they were not closed properly not all bytes will reach the files.
-        checkFileContent(F, expBytes);
-        checkFileContent(FB, expBytes);
+        checkFileContent(F, expBytes, true);
+        checkFileContent(FB, expBytes, true);
 
         // Expect that TextIOWrapper should have adjusted the line separator
-        checkFileContent(FT, newlineFix(expText));
+        checkFileContent(FT, newlineFix(expText), true);
     }
 
     /**
-     * Check the file contains the bytes we expect and <b>delete the file</b>. If it was not closed
-     * properly (layers in the right order and a flush) not all bytes will have reached the files.
+     * Check the file contains the bytes we expect and optionally <b>delete the file</b>. If it was
+     * not closed properly (layers in the right order and a flush) not all bytes will have reached
+     * the files.
      *
      * @param name of file
      * @param expBytes expected
+     * @param delete the file if true
      * @throws IOException if cannot open/read
      */
-    private static void checkFileContent(String name, byte[] expBytes) throws IOException {
+    private static void checkFileContent(String name, byte[] expBytes, boolean delete)
+            throws IOException {
+        // Open and read
         byte[] r = new byte[2 * expBytes.length];
         File f = new File(name);
         FileInputStream in = new FileInputStream(f);
         int n = in.read(r);
         in.close();
 
+        // Check as expected
         String msg = "Bytes read from " + name;
         assertEquals(msg, expBytes.length, n);
         byte[] resBytes = Arrays.copyOf(r, n);
         assertArrayEquals(msg, expBytes, resBytes);
 
-        f.delete();
+        // Delete the file
+        if (delete) {
+            f.delete();
+        }
     }
 
-
     /**
      * Replace "\n" characters by the system-defined newline sequence and return as bytes.
+     *
      * @param expText to translate
      * @return result as bytes
      */

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


More information about the Jython-checkins mailing list