[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 "",
"\/\\\"\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