diff options
135 files changed, 3952 insertions, 1537 deletions
diff --git a/Doc/distutils/examples.rst b/Doc/distutils/examples.rst index b268486398..5eb654a529 100644 --- a/Doc/distutils/examples.rst +++ b/Doc/distutils/examples.rst @@ -284,6 +284,48 @@ by using the :mod:`docutils` parser:: warning: check: Title underline too short. (line 2) warning: check: Could not finish the parsing. +Reading the metadata +===================== + +The :func:`distutils.core.setup` function provides a command-line interface +that allows you to query the metadata fields of a project through the +`setup.py` script of a given project:: + + $ python setup.py --name + distribute + +This call reads the `name` metadata by running the +:func:`distutils.core.setup` function. Although, when a source or binary +distribution is created with Distutils, the metadata fields are written +in a static file called :file:`PKG-INFO`. When a Distutils-based project is +installed in Python, the :file:`PKG-INFO` file is copied alongside the modules +and packages of the distribution under :file:`NAME-VERSION-pyX.X.egg-info`, +where `NAME` is the name of the project, `VERSION` its version as defined +in the Metadata, and `pyX.X` the major and minor version of Python like +`2.7` or `3.2`. + +You can read back this static file, by using the +:class:`distutils.dist.DistributionMetadata` class and its +:func:`read_pkg_file` method:: + + >>> from distutils.dist import DistributionMetadata + >>> metadata = DistributionMetadata() + >>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info')) + >>> metadata.name + 'distribute' + >>> metadata.version + '0.6.8' + >>> metadata.description + 'Easily download, build, install, upgrade, and uninstall Python packages' + +Notice that the class can also be instanciated with a metadata file path to +loads its values:: + + >>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info' + >>> DistributionMetadata(pkg_info_path).name + 'distribute' + + .. % \section{Multiple extension modules} .. % \label{multiple-ext} diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 83b479c6f9..3f256e1de3 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1098,7 +1098,7 @@ Python 3.2 or later. .. _custom-logrecord: -Customising ``LogRecord`` +Customizing ``LogRecord`` ------------------------- Every logging event is represented by a :class:`LogRecord` instance. @@ -1315,7 +1315,7 @@ of the Django documentation. .. _cookbook-rotator-namer: -Using a rotator and namer to customise log rotation processing +Using a rotator and namer to customize log rotation processing -------------------------------------------------------------- An example of how you can define a namer and rotator is given in the following @@ -1694,3 +1694,138 @@ When the above script is run, it prints:: Note that the order of items might be different according to the version of Python used. +.. currentmodule:: logging.config + +Customizing handlers with :func:`dictConfig` +-------------------------------------------- + +There are times when you want to customize logging handlers in particular ways, +and if you use :func:`dictConfig` you may be able to do this without +subclassing. As an example, consider that you may want to set the ownership of a +log file. On POSIX, this is easily done using :func:`shutil.chown`, but the file +handlers in the stdlib don't offer built-in support. You can customize handler +creation using a plain function such as:: + + def owned_file_handler(filename, mode='a', encoding=None, owner=None): + if owner: + if not os.path.exists(filename): + open(filename, 'a').close() + shutil.chown(filename, *owner) + return logging.FileHandler(filename, mode, encoding) + +You can then specify, in a logging configuration passed to :func:`dictConfig`, +that a logging handler be created by calling this function:: + + LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'default': { + 'format': '%(asctime)s %(levelname)s %(name)s %(message)s' + }, + }, + 'handlers': { + 'file':{ + # The values below are popped from this dictionary and + # used to create the handler, set the handler's level and + # its formatter. + '()': owned_file_handler, + 'level':'DEBUG', + 'formatter': 'default', + # The values below are passed to the handler creator callable + # as keyword arguments. + 'owner': ['pulse', 'pulse'], + 'filename': 'chowntest.log', + 'mode': 'w', + 'encoding': 'utf-8', + }, + }, + 'root': { + 'handlers': ['file'], + 'level': 'DEBUG', + }, + } + +In this example I am setting the ownership using the ``pulse`` user and group, +just for the purposes of illustration. Putting it together into a working +script, ``chowntest.py``:: + + import logging, logging.config, os, shutil + + def owned_file_handler(filename, mode='a', encoding=None, owner=None): + if owner: + if not os.path.exists(filename): + open(filename, 'a').close() + shutil.chown(filename, *owner) + return logging.FileHandler(filename, mode, encoding) + + LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'default': { + 'format': '%(asctime)s %(levelname)s %(name)s %(message)s' + }, + }, + 'handlers': { + 'file':{ + # The values below are popped from this dictionary and + # used to create the handler, set the handler's level and + # its formatter. + '()': owned_file_handler, + 'level':'DEBUG', + 'formatter': 'default', + # The values below are passed to the handler creator callable + # as keyword arguments. + 'owner': ['pulse', 'pulse'], + 'filename': 'chowntest.log', + 'mode': 'w', + 'encoding': 'utf-8', + }, + }, + 'root': { + 'handlers': ['file'], + 'level': 'DEBUG', + }, + } + + logging.config.dictConfig(LOGGING) + logger = logging.getLogger('mylogger') + logger.debug('A debug message') + +To run this, you will probably need to run as ``root``:: + + $ sudo python3.3 chowntest.py + $ cat chowntest.log + 2013-11-05 09:34:51,128 DEBUG mylogger A debug message + $ ls -l chowntest.log + -rw-r--r-- 1 pulse pulse 55 2013-11-05 09:34 chowntest.log + +Note that this example uses Python 3.3 because that's where :func:`shutil.chown` +makes an appearance. This approach should work with any Python version that +supports :func:`dictConfig` - namely, Python 2.7, 3.2 or later. With pre-3.3 +versions, you would need to implement the actual ownership change using e.g. +:func:`os.chown`. + +In practice, the handler-creating function may be in a utility module somewhere +in your project. Instead of the line in the configuration:: + + '()': owned_file_handler, + +you could use e.g.:: + + '()': 'ext://project.util.owned_file_handler', + +where ``project.util`` can be replaced with the actual name of the package +where the function resides. In the above working script, using +``'ext://__main__.owned_file_handler'`` should work. Here, the actual callable +is resolved by :func:`dictConfig` from the ``ext://`` specification. + +This example hopefully also points the way to how you could implement other +types of file change - e.g. setting specific POSIX permission bits - in the +same way, using :func:`os.chmod`. + +Of course, the approach could also be extended to types of handler other than a +:class:`~logging.FileHandler` - for example, one of the rotating file handlers, +or a different type of handler altogether. + diff --git a/Doc/howto/unicode.rst b/Doc/howto/unicode.rst index 2920aa11cb..9d48a787e8 100644 --- a/Doc/howto/unicode.rst +++ b/Doc/howto/unicode.rst @@ -514,7 +514,7 @@ columns and can return Unicode values from an SQL query. Unicode data is usually converted to a particular encoding before it gets written to disk or sent over a socket. It's possible to do all the work -yourself: open a file, read an 8-bit bytes object from it, and convert the string +yourself: open a file, read an 8-bit bytes object from it, and convert the bytes with ``bytes.decode(encoding)``. However, the manual approach is not recommended. One problem is the multi-byte nature of encodings; one Unicode character can be diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst index 02ec5d854a..c92a8e160a 100644 --- a/Doc/library/binascii.rst +++ b/Doc/library/binascii.rst @@ -145,7 +145,7 @@ The :mod:`binascii` module defines the following functions: Return the hexadecimal representation of the binary *data*. Every byte of *data* is converted into the corresponding 2-digit hex representation. The - resulting string is therefore twice as long as the length of *data*. + returned bytes object is therefore twice as long as the length of *data*. .. function:: a2b_hex(hexstr) diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index cd1915d03b..314636e4b4 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -377,7 +377,7 @@ The module :mod:`curses` defines the following functions: is to be displayed. -.. function:: newwin(begin_y, begin_x) +.. function:: newwin(nlines, ncols) newwin(nlines, ncols, begin_y, begin_x) Return a new window, whose left-upper corner is at ``(begin_y, begin_x)``, and diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 6ca15b71ae..065e850756 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1896,7 +1896,7 @@ Notes: making assumptions about the output value. Field orderings will vary (for example, "month/day/year" versus "day/month/year"), and the output may contain Unicode characters encoded using the locale's default encoding (for - example, if the current locale is ``js_JP``, the default encoding could be + example, if the current locale is ``ja_JP``, the default encoding could be any one of ``eucJP``, ``SJIS``, or ``utf-8``; use :meth:`locale.getlocale` to determine the current locale's encoding). diff --git a/Doc/library/fcntl.rst b/Doc/library/fcntl.rst index cc7734023e..8e932fb954 100644 --- a/Doc/library/fcntl.rst +++ b/Doc/library/fcntl.rst @@ -30,11 +30,11 @@ The module defines the following functions: .. function:: fcntl(fd, op[, arg]) - Perform the requested operation on file descriptor *fd* (file objects providing - a :meth:`~io.IOBase.fileno` method are accepted as well). The operation is - defined by *op* - and is operating system dependent. These codes are also found in the - :mod:`fcntl` module. The argument *arg* is optional, and defaults to the integer + Perform the operation *op* on file descriptor *fd* (file objects providing + a :meth:`~io.IOBase.fileno` method are accepted as well). The values used + for *op* are operating system dependent, and are available as constants + in the :mod:`fcntl` module, using the same names as used in the relevant C + header files. The argument *arg* is optional, and defaults to the integer value ``0``. When present, it can either be an integer value, or a string. With the argument missing or an integer value, the return value of this function is the integer return value of the C :c:func:`fcntl` call. When the argument is @@ -56,6 +56,9 @@ The module defines the following functions: that the argument handling is even more complicated. The op parameter is limited to values that can fit in 32-bits. + Additional constants of interest for use as the *op* argument can be + found in the :mod:`termios` module, under the same names as used in + the relevant C header files. The parameter *arg* can be one of an integer, absent (treated identically to the integer ``0``), an object supporting the read-only buffer interface (most likely diff --git a/Doc/library/gettext.rst b/Doc/library/gettext.rst index 825311bfdc..1efa54fcd8 100644 --- a/Doc/library/gettext.rst +++ b/Doc/library/gettext.rst @@ -3,8 +3,8 @@ .. module:: gettext :synopsis: Multilingual internationalization services. -.. moduleauthor:: Barry A. Warsaw <barry@zope.com> -.. sectionauthor:: Barry A. Warsaw <barry@zope.com> +.. moduleauthor:: Barry A. Warsaw <barry@python.org> +.. sectionauthor:: Barry A. Warsaw <barry@python.org> **Source code:** :source:`Lib/gettext.py` diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index 81addb3908..d0dc5721ba 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -892,7 +892,7 @@ possible, while any potentially slow operations (such as sending an email via Enqueues the record on the queue using ``put_nowait()``; you may want to override this if you want to use blocking behaviour, or a - timeout, or a customised queue implementation. + timeout, or a customized queue implementation. diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 4521183929..e9df25eea7 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -794,17 +794,18 @@ information into logging calls. For a usage example , see the section on (possibly modified) versions of the arguments passed in. In addition to the above, :class:`LoggerAdapter` supports the following -methods of :class:`Logger`, i.e. :meth:`debug`, :meth:`info`, :meth:`warning`, -:meth:`error`, :meth:`exception`, :meth:`critical`, :meth:`log`, -:meth:`isEnabledFor`, :meth:`getEffectiveLevel`, :meth:`setLevel`, -:meth:`hasHandlers`. These methods have the same signatures as their +methods of :class:`Logger`: :meth:`~Logger.debug`, :meth:`~Logger.info`, +:meth:`~Logger.warning`, :meth:`~Logger.error`, :meth:`~Logger.exception`, +:meth:`~Logger.critical`, :meth:`~Logger.log`, :meth:`~Logger.isEnabledFor`, +:meth:`~Logger.getEffectiveLevel`, :meth:`~Logger.setLevel` and +:meth:`~Logger.hasHandlers`. These methods have the same signatures as their counterparts in :class:`Logger`, so you can use the two types of instances interchangeably. .. versionchanged:: 3.2 - The :meth:`isEnabledFor`, :meth:`getEffectiveLevel`, :meth:`setLevel` and - :meth:`hasHandlers` methods were added to :class:`LoggerAdapter`. These - methods delegate to the underlying logger. + The :meth:`~Logger.isEnabledFor`, :meth:`~Logger.getEffectiveLevel`, + :meth:`~Logger.setLevel` and :meth:`~Logger.hasHandlers` methods were added + to :class:`LoggerAdapter`. These methods delegate to the underlying logger. Thread Safety @@ -844,8 +845,8 @@ functions. Return either the standard :class:`Logger` class, or the last class passed to :func:`setLoggerClass`. This function may be called from within a new class - definition, to ensure that installing a customised :class:`Logger` class will - not undo customisations already applied by other code. For example:: + definition, to ensure that installing a customized :class:`Logger` class will + not undo customizations already applied by other code. For example:: class MyLogger(logging.getLoggerClass()): # ... override behaviour here diff --git a/Doc/library/nntplib.rst b/Doc/library/nntplib.rst index 1d1aa409c5..759d3b7b29 100644 --- a/Doc/library/nntplib.rst +++ b/Doc/library/nntplib.rst @@ -395,18 +395,18 @@ tuples or objects that the method normally returns will be empty. .. method:: NNTP.next() - Send a ``NEXT`` command. Return as for :meth:`stat`. + Send a ``NEXT`` command. Return as for :meth:`.stat`. .. method:: NNTP.last() - Send a ``LAST`` command. Return as for :meth:`stat`. + Send a ``LAST`` command. Return as for :meth:`.stat`. .. method:: NNTP.article(message_spec=None, *, file=None) Send an ``ARTICLE`` command, where *message_spec* has the same meaning as - for :meth:`stat`. Return a tuple ``(response, info)`` where *info* + for :meth:`.stat`. Return a tuple ``(response, info)`` where *info* is a :class:`~collections.namedtuple` with three attributes *number*, *message_id* and *lines* (in that order). *number* is the article number in the group (or 0 if the information is not available), *message_id* the diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 59457f75ac..2c33366c0d 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -2582,6 +2582,10 @@ written in Python, such as a mail server's external command delivery program. Note that some platforms including FreeBSD <= 6.3, Cygwin and OS/2 EMX have known issues when using fork() from a thread. + .. warning:: + + See :mod:`ssl` for applications that use the SSL module with fork(). + Availability: Unix. diff --git a/Doc/library/pickle.rst b/Doc/library/pickle.rst index 0354a3013d..a99117c72a 100644 --- a/Doc/library/pickle.rst +++ b/Doc/library/pickle.rst @@ -12,7 +12,7 @@ .. module:: pickle :synopsis: Convert Python objects to streams of bytes and back. .. sectionauthor:: Jim Kerr <jbkerr@sr.hp.com>. -.. sectionauthor:: Barry Warsaw <barry@zope.com> +.. sectionauthor:: Barry Warsaw <barry@python.org> The :mod:`pickle` module implements a fundamental, but powerful algorithm for diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst index 2ca71ffc3c..f3058187d4 100644 --- a/Doc/library/smtpd.rst +++ b/Doc/library/smtpd.rst @@ -4,7 +4,7 @@ .. module:: smtpd :synopsis: A SMTP server implementation in Python. -.. moduleauthor:: Barry Warsaw <barry@zope.com> +.. moduleauthor:: Barry Warsaw <barry@python.org> .. sectionauthor:: Moshe Zadka <moshez@moshez.org> **Source code:** :source:`Lib/smtpd.py` diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index a688e4609d..1c5c355160 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -28,6 +28,14 @@ probably additional platforms, as long as OpenSSL is installed on that platform. operating system socket APIs. The installed version of OpenSSL may also cause variations in behavior. +.. warning:: + + OpenSSL's internal random number generator does not properly handle fork. + Applications must change the PRNG state of the parent process if they use + any SSL feature with :func:`os.fork`. Any successful call of + :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or + :func:`~ssl.RAND_pseudo_bytes` is sufficient. + This section documents the objects and functions in the ``ssl`` module; for more general information about TLS, SSL, and certificates, the reader is referred to the documents in the "See Also" section at the bottom. diff --git a/Doc/library/string.rst b/Doc/library/string.rst index e304c5d4ff..e5bab684df 100644 --- a/Doc/library/string.rst +++ b/Doc/library/string.rst @@ -300,7 +300,7 @@ The general form of a *standard format specifier* is: precision: `integer` type: "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%" -If a valid *align* value is specified, it can be preceeded by a *fill* +If a valid *align* value is specified, it can be preceded by a *fill* character that can be any character and defaults to a space if omitted. Note that it is not possible to use ``{`` and ``}`` as *fill* char while using the :meth:`str.format` method; this limitation however doesn't diff --git a/Doc/library/subprocess.rst b/Doc/library/subprocess.rst index f115634daf..49a365775f 100644 --- a/Doc/library/subprocess.rst +++ b/Doc/library/subprocess.rst @@ -1050,10 +1050,12 @@ handling consistency are valid for these functions. Return ``(status, output)`` of executing *cmd* in a shell. - Execute the string *cmd* in a shell with :func:`os.popen` and return a 2-tuple - ``(status, output)``. *cmd* is actually run as ``{ cmd ; } 2>&1``, so that the - returned output will contain output or error messages. A trailing newline is - stripped from the output. The exit status for the command can be interpreted + Execute the string *cmd* in a shell with :class:`Popen` and return a 2-tuple + ``(status, output)`` via :func:`Popen.communicate`. Universal newlines mode + is used; see the notes on :ref:`frequently-used-arguments` for more details. + + A trailing newline is stripped from the output. + The exit status for the command can be interpreted according to the rules for the C function :c:func:`wait`. Example:: >>> subprocess.getstatusoutput('ls /bin/ls') @@ -1063,7 +1065,8 @@ handling consistency are valid for these functions. >>> subprocess.getstatusoutput('/bin/junk') (256, 'sh: /bin/junk: not found') - Availability: UNIX. + .. versionchanged:: 3.3 + Availability: Unix & Windows .. function:: getoutput(cmd) @@ -1076,7 +1079,8 @@ handling consistency are valid for these functions. >>> subprocess.getoutput('ls /bin/ls') '/bin/ls' - Availability: UNIX. + .. versionchanged:: 3.3 + Availability: Unix & Windows Notes diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 86739615eb..cf652199e3 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -379,7 +379,7 @@ Functions Parses an XML section into an element tree incrementally, and reports what's going on to the user. *source* is a filename or :term:`file object` - containing XML data. *events* is a list of events to report back. The + containing XML data. *events* is a tuple of events to report back. The supported events are the strings ``"start"``, ``"end"``, ``"start-ns"`` and ``"end-ns"`` (the "ns" events are used to get detailed namespace information). If *events* is omitted, only ``"end"`` events are reported. diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py index cfc219e56b..99b97aa7d8 100644 --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -84,8 +84,8 @@ class ArrayTestCase(unittest.TestCase): self.assertEqual(values, [1, 2, 3, 4, 5]) def test_classcache(self): - self.assertTrue(not ARRAY(c_int, 3) is ARRAY(c_int, 4)) - self.assertTrue(ARRAY(c_int, 3) is ARRAY(c_int, 3)) + self.assertIsNot(ARRAY(c_int, 3), ARRAY(c_int, 4)) + self.assertIs(ARRAY(c_int, 3), ARRAY(c_int, 3)) def test_from_address(self): # Failed with 0.9.8, reported by JUrner @@ -125,7 +125,7 @@ class ArrayTestCase(unittest.TestCase): # Create a new array type based on it: t1 = my_int * 1 t2 = my_int * 1 - self.assertTrue(t1 is t2) + self.assertIs(t1, t2) def test_subclass(self): class T(Array): diff --git a/Lib/ctypes/test/test_as_parameter.py b/Lib/ctypes/test/test_as_parameter.py index 2657a66d11..43703e3b6e 100644 --- a/Lib/ctypes/test/test_as_parameter.py +++ b/Lib/ctypes/test/test_as_parameter.py @@ -134,7 +134,7 @@ class BasicWrapTestCase(unittest.TestCase): f.argtypes = [c_longlong, MyCallback] def callback(value): - self.assertTrue(isinstance(value, int)) + self.assertIsInstance(value, int) return value & 0x7FFFFFFF cb = MyCallback(callback) diff --git a/Lib/ctypes/test/test_buffers.py b/Lib/ctypes/test/test_buffers.py index 2dc74841c4..0d12f47afe 100644 --- a/Lib/ctypes/test/test_buffers.py +++ b/Lib/ctypes/test/test_buffers.py @@ -7,12 +7,12 @@ class StringBufferTestCase(unittest.TestCase): b = create_string_buffer(32) self.assertEqual(len(b), 32) self.assertEqual(sizeof(b), 32 * sizeof(c_char)) - self.assertTrue(type(b[0]) is bytes) + self.assertIs(type(b[0]), bytes) b = create_string_buffer(b"abc") self.assertEqual(len(b), 4) # trailing nul char self.assertEqual(sizeof(b), 4 * sizeof(c_char)) - self.assertTrue(type(b[0]) is bytes) + self.assertIs(type(b[0]), bytes) self.assertEqual(b[0], b"a") self.assertEqual(b[:], b"abc\0") self.assertEqual(b[::], b"abc\0") @@ -33,12 +33,12 @@ class StringBufferTestCase(unittest.TestCase): b = create_unicode_buffer(32) self.assertEqual(len(b), 32) self.assertEqual(sizeof(b), 32 * sizeof(c_wchar)) - self.assertTrue(type(b[0]) is str) + self.assertIs(type(b[0]), str) b = create_unicode_buffer("abc") self.assertEqual(len(b), 4) # trailing nul char self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertTrue(type(b[0]) is str) + self.assertIs(type(b[0]), str) self.assertEqual(b[0], "a") self.assertEqual(b[:], "abc\0") self.assertEqual(b[::], "abc\0") @@ -50,7 +50,7 @@ class StringBufferTestCase(unittest.TestCase): b = create_unicode_buffer("abc") self.assertEqual(len(b), 4) # trailing nul char self.assertEqual(sizeof(b), 4 * sizeof(c_wchar)) - self.assertTrue(type(b[0]) is str) + self.assertIs(type(b[0]), str) self.assertEqual(b[0], "a") self.assertEqual(b[:], "abc\0") self.assertEqual(b[::], "abc\0") diff --git a/Lib/ctypes/test/test_byteswap.py b/Lib/ctypes/test/test_byteswap.py index 0d2974fdd9..63dde13519 100644 --- a/Lib/ctypes/test/test_byteswap.py +++ b/Lib/ctypes/test/test_byteswap.py @@ -23,11 +23,11 @@ class Test(unittest.TestCase): def test_endian_short(self): if sys.byteorder == "little": - self.assertTrue(c_short.__ctype_le__ is c_short) - self.assertTrue(c_short.__ctype_be__.__ctype_le__ is c_short) + self.assertIs(c_short.__ctype_le__, c_short) + self.assertIs(c_short.__ctype_be__.__ctype_le__, c_short) else: - self.assertTrue(c_short.__ctype_be__ is c_short) - self.assertTrue(c_short.__ctype_le__.__ctype_be__ is c_short) + self.assertIs(c_short.__ctype_be__, c_short) + self.assertIs(c_short.__ctype_le__.__ctype_be__, c_short) s = c_short.__ctype_be__(0x1234) self.assertEqual(bin(struct.pack(">h", 0x1234)), "1234") self.assertEqual(bin(s), "1234") @@ -50,11 +50,11 @@ class Test(unittest.TestCase): def test_endian_int(self): if sys.byteorder == "little": - self.assertTrue(c_int.__ctype_le__ is c_int) - self.assertTrue(c_int.__ctype_be__.__ctype_le__ is c_int) + self.assertIs(c_int.__ctype_le__, c_int) + self.assertIs(c_int.__ctype_be__.__ctype_le__, c_int) else: - self.assertTrue(c_int.__ctype_be__ is c_int) - self.assertTrue(c_int.__ctype_le__.__ctype_be__ is c_int) + self.assertIs(c_int.__ctype_be__, c_int) + self.assertIs(c_int.__ctype_le__.__ctype_be__, c_int) s = c_int.__ctype_be__(0x12345678) self.assertEqual(bin(struct.pack(">i", 0x12345678)), "12345678") @@ -78,11 +78,11 @@ class Test(unittest.TestCase): def test_endian_longlong(self): if sys.byteorder == "little": - self.assertTrue(c_longlong.__ctype_le__ is c_longlong) - self.assertTrue(c_longlong.__ctype_be__.__ctype_le__ is c_longlong) + self.assertIs(c_longlong.__ctype_le__, c_longlong) + self.assertIs(c_longlong.__ctype_be__.__ctype_le__, c_longlong) else: - self.assertTrue(c_longlong.__ctype_be__ is c_longlong) - self.assertTrue(c_longlong.__ctype_le__.__ctype_be__ is c_longlong) + self.assertIs(c_longlong.__ctype_be__, c_longlong) + self.assertIs(c_longlong.__ctype_le__.__ctype_be__, c_longlong) s = c_longlong.__ctype_be__(0x1234567890ABCDEF) self.assertEqual(bin(struct.pack(">q", 0x1234567890ABCDEF)), "1234567890ABCDEF") @@ -106,11 +106,11 @@ class Test(unittest.TestCase): def test_endian_float(self): if sys.byteorder == "little": - self.assertTrue(c_float.__ctype_le__ is c_float) - self.assertTrue(c_float.__ctype_be__.__ctype_le__ is c_float) + self.assertIs(c_float.__ctype_le__, c_float) + self.assertIs(c_float.__ctype_be__.__ctype_le__, c_float) else: - self.assertTrue(c_float.__ctype_be__ is c_float) - self.assertTrue(c_float.__ctype_le__.__ctype_be__ is c_float) + self.assertIs(c_float.__ctype_be__, c_float) + self.assertIs(c_float.__ctype_le__.__ctype_be__, c_float) s = c_float(math.pi) self.assertEqual(bin(struct.pack("f", math.pi)), bin(s)) # Hm, what's the precision of a float compared to a double? @@ -124,11 +124,11 @@ class Test(unittest.TestCase): def test_endian_double(self): if sys.byteorder == "little": - self.assertTrue(c_double.__ctype_le__ is c_double) - self.assertTrue(c_double.__ctype_be__.__ctype_le__ is c_double) + self.assertIs(c_double.__ctype_le__, c_double) + self.assertIs(c_double.__ctype_be__.__ctype_le__, c_double) else: - self.assertTrue(c_double.__ctype_be__ is c_double) - self.assertTrue(c_double.__ctype_le__.__ctype_be__ is c_double) + self.assertIs(c_double.__ctype_be__, c_double) + self.assertIs(c_double.__ctype_le__.__ctype_be__, c_double) s = c_double(math.pi) self.assertEqual(s.value, math.pi) self.assertEqual(bin(struct.pack("d", math.pi)), bin(s)) @@ -140,14 +140,14 @@ class Test(unittest.TestCase): self.assertEqual(bin(struct.pack(">d", math.pi)), bin(s)) def test_endian_other(self): - self.assertTrue(c_byte.__ctype_le__ is c_byte) - self.assertTrue(c_byte.__ctype_be__ is c_byte) + self.assertIs(c_byte.__ctype_le__, c_byte) + self.assertIs(c_byte.__ctype_be__, c_byte) - self.assertTrue(c_ubyte.__ctype_le__ is c_ubyte) - self.assertTrue(c_ubyte.__ctype_be__ is c_ubyte) + self.assertIs(c_ubyte.__ctype_le__, c_ubyte) + self.assertIs(c_ubyte.__ctype_be__, c_ubyte) - self.assertTrue(c_char.__ctype_le__ is c_char) - self.assertTrue(c_char.__ctype_be__ is c_char) + self.assertIs(c_char.__ctype_le__, c_char) + self.assertIs(c_char.__ctype_be__, c_char) def test_struct_fields_1(self): if sys.byteorder == "little": diff --git a/Lib/ctypes/test/test_cast.py b/Lib/ctypes/test/test_cast.py index 702de3cffa..32496f6863 100644 --- a/Lib/ctypes/test/test_cast.py +++ b/Lib/ctypes/test/test_cast.py @@ -38,14 +38,14 @@ class Test(unittest.TestCase): p = cast(array, POINTER(c_char_p)) # array and p share a common _objects attribute - self.assertTrue(p._objects is array._objects) + self.assertIs(p._objects, array._objects) self.assertEqual(array._objects, {'0': b"foo bar", id(array): array}) p[0] = b"spam spam" self.assertEqual(p._objects, {'0': b"spam spam", id(array): array}) - self.assertTrue(array._objects is p._objects) + self.assertIs(array._objects, p._objects) p[1] = b"foo bar" self.assertEqual(p._objects, {'1': b'foo bar', '0': b"spam spam", id(array): array}) - self.assertTrue(array._objects is p._objects) + self.assertIs(array._objects, p._objects) def test_other(self): p = cast((c_int * 4)(1, 2, 3, 4), POINTER(c_int)) diff --git a/Lib/ctypes/test/test_frombuffer.py b/Lib/ctypes/test/test_frombuffer.py index 92d61580dd..ffb27a6ea3 100644 --- a/Lib/ctypes/test/test_frombuffer.py +++ b/Lib/ctypes/test/test_frombuffer.py @@ -23,7 +23,7 @@ class Test(unittest.TestCase): a[0], a[-1] = 200, -200 self.assertEqual(x[:], a.tolist()) - self.assertTrue(a in x._objects.values()) + self.assertIn(a, x._objects.values()) self.assertRaises(ValueError, c_int.from_buffer, a, -1) diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py index 13bf4088bf..ff25c8febd 100644 --- a/Lib/ctypes/test/test_funcptr.py +++ b/Lib/ctypes/test/test_funcptr.py @@ -75,7 +75,7 @@ class CFuncPtrTestCase(unittest.TestCase): ## "lpfnWndProc", WNDPROC_2(wndproc)) # instead: - self.assertTrue(WNDPROC is WNDPROC_2) + self.assertIs(WNDPROC, WNDPROC_2) # 'wndclass.lpfnWndProc' leaks 94 references. Why? self.assertEqual(wndclass.lpfnWndProc(1, 2, 3, 4), 10) diff --git a/Lib/ctypes/test/test_functions.py b/Lib/ctypes/test/test_functions.py index 45d74ec9e8..07eeb68924 100644 --- a/Lib/ctypes/test/test_functions.py +++ b/Lib/ctypes/test/test_functions.py @@ -306,7 +306,7 @@ class FunctionTestCase(unittest.TestCase): f.argtypes = [c_longlong, MyCallback] def callback(value): - self.assertTrue(isinstance(value, int)) + self.assertIsInstance(value, int) return value & 0x7FFFFFFF cb = MyCallback(callback) diff --git a/Lib/ctypes/test/test_loading.py b/Lib/ctypes/test/test_loading.py index 4029b463bc..414363d8c8 100644 --- a/Lib/ctypes/test/test_loading.py +++ b/Lib/ctypes/test/test_loading.py @@ -43,7 +43,7 @@ class LoaderTest(unittest.TestCase): if os.name in ("nt", "ce"): def test_load_library(self): - self.assertFalse(libc_name is None) + self.assertIsNotNone(libc_name) if is_resource_enabled("printing"): print(find_library("kernel32")) print(find_library("user32")) diff --git a/Lib/ctypes/test/test_numbers.py b/Lib/ctypes/test/test_numbers.py index c02abf9c32..3b7194f31c 100644 --- a/Lib/ctypes/test/test_numbers.py +++ b/Lib/ctypes/test/test_numbers.py @@ -181,10 +181,10 @@ class NumberTestCase(unittest.TestCase): a = array(t._type_, [3.14]) v = t.from_address(a.buffer_info()[0]) self.assertEqual(v.value, a[0]) - self.assertTrue(type(v) is t) + self.assertIs(type(v), t) a[0] = 2.3456e17 self.assertEqual(v.value, a[0]) - self.assertTrue(type(v) is t) + self.assertIs(type(v), t) def test_char_from_address(self): from ctypes import c_char @@ -194,7 +194,7 @@ class NumberTestCase(unittest.TestCase): a[0] = ord('x') v = c_char.from_address(a.buffer_info()[0]) self.assertEqual(v.value, b'x') - self.assertTrue(type(v) is c_char) + self.assertIs(type(v), c_char) a[0] = ord('?') self.assertEqual(v.value, b'?') diff --git a/Lib/ctypes/test/test_parameters.py b/Lib/ctypes/test/test_parameters.py index 9762fb915d..12b5bd5fba 100644 --- a/Lib/ctypes/test/test_parameters.py +++ b/Lib/ctypes/test/test_parameters.py @@ -54,7 +54,7 @@ class SimpleTypesTestCase(unittest.TestCase): # c_char_p.from_param on a Python String packs the string # into a cparam object s = b"123" - self.assertTrue(c_char_p.from_param(s)._obj is s) + self.assertIs(c_char_p.from_param(s)._obj, s) # new in 0.9.1: convert (encode) unicode to ascii self.assertEqual(c_char_p.from_param(b"123")._obj, b"123") @@ -64,7 +64,7 @@ class SimpleTypesTestCase(unittest.TestCase): # calling c_char_p.from_param with a c_char_p instance # returns the argument itself: a = c_char_p(b"123") - self.assertTrue(c_char_p.from_param(a) is a) + self.assertIs(c_char_p.from_param(a), a) def test_cw_strings(self): from ctypes import byref diff --git a/Lib/ctypes/test/test_pointers.py b/Lib/ctypes/test/test_pointers.py index d29317a3e4..f8ef0ab830 100644 --- a/Lib/ctypes/test/test_pointers.py +++ b/Lib/ctypes/test/test_pointers.py @@ -78,7 +78,7 @@ class PointersTestCase(unittest.TestCase): ## i = c_int(42) ## callback(byref(i)) -## self.assertTrue(i.value == 84) +## self.assertEqual(i.value, 84) doit(callback) ## print self.result @@ -91,11 +91,11 @@ class PointersTestCase(unittest.TestCase): i = ct(42) p = pointer(i) ## print type(p.contents), ct - self.assertTrue(type(p.contents) is ct) + self.assertIs(type(p.contents), ct) # p.contents is the same as p[0] ## print p.contents -## self.assertTrue(p.contents == 42) -## self.assertTrue(p[0] == 42) +## self.assertEqual(p.contents, 42) +## self.assertEqual(p[0], 42) self.assertRaises(TypeError, delitem, p, 0) diff --git a/Lib/ctypes/test/test_python_api.py b/Lib/ctypes/test/test_python_api.py index 9de3980ed4..5eb882af5e 100644 --- a/Lib/ctypes/test/test_python_api.py +++ b/Lib/ctypes/test/test_python_api.py @@ -64,7 +64,7 @@ class PythonAPITestCase(unittest.TestCase): ref = grc(s) # id(python-object) is the address pyobj = PyObj_FromPtr(id(s)) - self.assertTrue(s is pyobj) + self.assertIs(s, pyobj) self.assertEqual(grc(s), ref + 1) del pyobj diff --git a/Lib/ctypes/test/test_refcounts.py b/Lib/ctypes/test/test_refcounts.py index a483bb0bbc..f2edfa6400 100644 --- a/Lib/ctypes/test/test_refcounts.py +++ b/Lib/ctypes/test/test_refcounts.py @@ -26,7 +26,7 @@ class RefcountTestCase(unittest.TestCase): self.assertEqual(grc(callback), 2) cb = MyCallback(callback) - self.assertTrue(grc(callback) > 2) + self.assertGreater(grc(callback), 2) result = f(-10, cb) self.assertEqual(result, -18) cb = None @@ -46,15 +46,15 @@ class RefcountTestCase(unittest.TestCase): # the CFuncPtr instance holds at least one refcount on func: f = OtherCallback(func) - self.assertTrue(grc(func) > 2) + self.assertGreater(grc(func), 2) # and may release it again del f - self.assertTrue(grc(func) >= 2) + self.assertGreaterEqual(grc(func), 2) # but now it must be gone gc.collect() - self.assertTrue(grc(func) == 2) + self.assertEqual(grc(func), 2) class X(ctypes.Structure): _fields_ = [("a", OtherCallback)] @@ -62,11 +62,11 @@ class RefcountTestCase(unittest.TestCase): x.a = OtherCallback(func) # the CFuncPtr instance holds at least one refcount on func: - self.assertTrue(grc(func) > 2) + self.assertGreater(grc(func), 2) # and may release it again del x - self.assertTrue(grc(func) >= 2) + self.assertGreaterEqual(grc(func), 2) # and now it must be gone again gc.collect() @@ -75,7 +75,7 @@ class RefcountTestCase(unittest.TestCase): f = OtherCallback(func) # the CFuncPtr instance holds at least one refcount on func: - self.assertTrue(grc(func) > 2) + self.assertGreater(grc(func), 2) # create a cycle f.cycle = f diff --git a/Lib/ctypes/test/test_strings.py b/Lib/ctypes/test/test_strings.py index 1a9bdbc5c0..9dc2a291a7 100644 --- a/Lib/ctypes/test/test_strings.py +++ b/Lib/ctypes/test/test_strings.py @@ -115,24 +115,24 @@ class StringTestCase(unittest.TestCase): # New in releases later than 0.4.0: # c_string(number) returns an empty string of size number - self.assertTrue(len(c_string(32).raw) == 32) + self.assertEqual(len(c_string(32).raw), 32) self.assertRaises(ValueError, c_string, -1) self.assertRaises(ValueError, c_string, 0) # These tests fail, because it is no longer initialized -## self.assertTrue(c_string(2).value == "") -## self.assertTrue(c_string(2).raw == "\000\000") - self.assertTrue(c_string(2).raw[-1] == "\000") - self.assertTrue(len(c_string(2).raw) == 2) +## self.assertEqual(c_string(2).value, "") +## self.assertEqual(c_string(2).raw, "\000\000") + self.assertEqual(c_string(2).raw[-1], "\000") + self.assertEqual(len(c_string(2).raw), 2) def XX_test_initialized_strings(self): - self.assertTrue(c_string("ab", 4).raw[:2] == "ab") - self.assertTrue(c_string("ab", 4).raw[:2:] == "ab") - self.assertTrue(c_string("ab", 4).raw[:2:-1] == "ba") - self.assertTrue(c_string("ab", 4).raw[:2:2] == "a") - self.assertTrue(c_string("ab", 4).raw[-1] == "\000") - self.assertTrue(c_string("ab", 2).raw == "a\000") + self.assertEqual(c_string("ab", 4).raw[:2], "ab") + self.assertEqual(c_string("ab", 4).raw[:2:], "ab") + self.assertEqual(c_string("ab", 4).raw[:2:-1], "ba") + self.assertEqual(c_string("ab", 4).raw[:2:2], "a") + self.assertEqual(c_string("ab", 4).raw[-1], "\000") + self.assertEqual(c_string("ab", 2).raw, "a\000") def XX_test_toolong(self): cs = c_string("abcdef") @@ -163,22 +163,22 @@ else: # XXX This behaviour is about to change: # len returns the size of the internal buffer in bytes. # This includes the terminating NUL character. - self.assertTrue(sizeof(cs) == 14) + self.assertEqual(sizeof(cs), 14) # The value property is the string up to the first terminating NUL. - self.assertTrue(cs.value == "abcdef") - self.assertTrue(c_wstring("abc\000def").value == "abc") + self.assertEqual(cs.value, "abcdef") + self.assertEqual(c_wstring("abc\000def").value, "abc") - self.assertTrue(c_wstring("abc\000def").value == "abc") + self.assertEqual(c_wstring("abc\000def").value, "abc") # The raw property is the total buffer contents: - self.assertTrue(cs.raw == "abcdef\000") - self.assertTrue(c_wstring("abc\000def").raw == "abc\000def\000") + self.assertEqual(cs.raw, "abcdef\000") + self.assertEqual(c_wstring("abc\000def").raw, "abc\000def\000") # We can change the value: cs.value = "ab" - self.assertTrue(cs.value == "ab") - self.assertTrue(cs.raw == "ab\000\000\000\000\000") + self.assertEqual(cs.value, "ab") + self.assertEqual(cs.raw, "ab\000\000\000\000\000") self.assertRaises(TypeError, c_wstring, "123") self.assertRaises(ValueError, c_wstring, 0) diff --git a/Lib/ctypes/test/test_structures.py b/Lib/ctypes/test/test_structures.py index 9d5b99e2f8..61b9fe7c18 100644 --- a/Lib/ctypes/test/test_structures.py +++ b/Lib/ctypes/test/test_structures.py @@ -374,9 +374,9 @@ class StructureTestCase(unittest.TestCase): ## class X(Structure): ## _fields_ = [] - self.assertTrue("in_dll" in dir(type(Structure))) - self.assertTrue("from_address" in dir(type(Structure))) - self.assertTrue("in_dll" in dir(type(Structure))) + self.assertIn("in_dll", dir(type(Structure))) + self.assertIn("from_address", dir(type(Structure))) + self.assertIn("in_dll", dir(type(Structure))) def test_positional_args(self): # see also http://bugs.python.org/issue5042 @@ -446,8 +446,8 @@ class TestRecursiveStructure(unittest.TestCase): try: Recursive._fields_ = [("next", Recursive)] except AttributeError as details: - self.assertTrue("Structure or union cannot contain itself" in - str(details)) + self.assertIn("Structure or union cannot contain itself", + str(details)) else: self.fail("Structure or union cannot contain itself") @@ -463,8 +463,7 @@ class TestRecursiveStructure(unittest.TestCase): try: Second._fields_ = [("first", First)] except AttributeError as details: - self.assertTrue("_fields_ is final" in - str(details)) + self.assertIn("_fields_ is final", str(details)) else: self.fail("AttributeError not raised") diff --git a/Lib/distutils/command/build_py.py b/Lib/distutils/command/build_py.py index 1371b3d6ff..d48eb69900 100644 --- a/Lib/distutils/command/build_py.py +++ b/Lib/distutils/command/build_py.py @@ -127,7 +127,8 @@ class build_py (Command): # Each pattern has to be converted to a platform-specific path filelist = glob(os.path.join(src_dir, convert_path(pattern))) # Files that match more than one pattern are only added once - files.extend([fn for fn in filelist if fn not in files]) + files.extend([fn for fn in filelist if fn not in files + and os.path.isfile(fn)]) return files def build_package_data(self): diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py index 8b36851d25..d2bc82cea3 100644 --- a/Lib/distutils/command/upload.py +++ b/Lib/distutils/command/upload.py @@ -10,10 +10,9 @@ import sys import os, io import socket import platform -import configparser -import http.client as httpclient from base64 import standard_b64encode -import urllib.parse +from urllib.request import urlopen, Request, HTTPError +from urllib.parse import urlparse # this keeps compatibility for 2.3 and 2.4 if sys.version < "2.5": @@ -66,6 +65,15 @@ class upload(PyPIRCCommand): self.upload_file(command, pyversion, filename) def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] @@ -162,41 +170,31 @@ class upload(PyPIRCCommand): self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) # build the Request - # We can't use urllib since we need to send the Basic - # auth right with the first request - # TODO(jhylton): Can we fix urllib? - schema, netloc, url, params, query, fragments = \ - urllib.parse.urlparse(self.repository) - assert not params and not query and not fragments - if schema == 'http': - http = httpclient.HTTPConnection(netloc) - elif schema == 'https': - http = httpclient.HTTPSConnection(netloc) - else: - raise AssertionError("unsupported schema "+schema) - - data = '' - loglevel = log.INFO + headers = {'Content-type': + 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth} + + request = Request(self.repository, data=body, + headers=headers) + # send the data try: - http.connect() - http.putrequest("POST", url) - http.putheader('Content-type', - 'multipart/form-data; boundary=%s'%boundary) - http.putheader('Content-length', str(len(body))) - http.putheader('Authorization', auth) - http.endheaders() - http.send(body) + result = urlopen(request) + status = result.getcode() + reason = result.msg except socket.error as e: self.announce(str(e), log.ERROR) return + except HTTPError as e: + status = e.code + reason = e.msg - r = http.getresponse() - if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), + self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) + msg = '\n'.join(('-' * 75, result.read(), '-' * 75)) self.announce(msg, log.INFO) diff --git a/Lib/distutils/dist.py b/Lib/distutils/dist.py index f7fac08918..11a210279e 100644 --- a/Lib/distutils/dist.py +++ b/Lib/distutils/dist.py @@ -5,6 +5,7 @@ being built/installed/distributed. """ import sys, os, re +from email import message_from_file try: import warnings @@ -999,25 +1000,80 @@ class DistributionMetadata: "provides", "requires", "obsoletes", ) - def __init__ (self): - self.name = None - self.version = None - self.author = None - self.author_email = None + def __init__(self, path=None): + if path is not None: + self.read_pkg_file(open(path)) + else: + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None + + def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + metadata_version = msg['metadata-version'] + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') self.maintainer = None + self.author_email = _read_field('author-email') self.maintainer_email = None - self.url = None - self.license = None - self.description = None - self.long_description = None - self.keywords = None - self.platforms = None - self.classifiers = None - self.download_url = None - # PEP 314 - self.provides = None - self.requires = None - self.obsoletes = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if metadata_version == '1.1': + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py index 1afdd46225..d3fb24ad56 100644 --- a/Lib/distutils/tests/test_archive_util.py +++ b/Lib/distutils/tests/test_archive_util.py @@ -210,7 +210,7 @@ class ArchiveUtilTestCase(support.TempdirManager, dry_run=True) finally: os.chdir(old_dir) - self.assertTrue(not os.path.exists(tarball)) + self.assertFalse(os.path.exists(tarball)) self.assertEqual(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, diff --git a/Lib/distutils/tests/test_bdist_rpm.py b/Lib/distutils/tests/test_bdist_rpm.py index b090b79e0c..aa1445d10a 100644 --- a/Lib/distutils/tests/test_bdist_rpm.py +++ b/Lib/distutils/tests/test_bdist_rpm.py @@ -81,7 +81,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) # bug #2945: upload ignores bdist_rpm files self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) @@ -125,7 +125,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) # bug #2945: upload ignores bdist_rpm files self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) diff --git a/Lib/distutils/tests/test_bdist_wininst.py b/Lib/distutils/tests/test_bdist_wininst.py index f9e8f89e21..5d17ab19a9 100644 --- a/Lib/distutils/tests/test_bdist_wininst.py +++ b/Lib/distutils/tests/test_bdist_wininst.py @@ -22,7 +22,7 @@ class BuildWinInstTestCase(support.TempdirManager, # and make sure it finds it and returns its content # no matter what platform we have exe_file = cmd.get_exe_bytes() - self.assertTrue(len(exe_file) > 10) + self.assertGreater(len(exe_file), 10) def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) diff --git a/Lib/distutils/tests/test_build_clib.py b/Lib/distutils/tests/test_build_clib.py index ee1c04162b..c2b981f20e 100644 --- a/Lib/distutils/tests/test_build_clib.py +++ b/Lib/distutils/tests/test_build_clib.py @@ -137,7 +137,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertTrue('libfoo.a' in os.listdir(build_temp)) + self.assertIn('libfoo.a', os.listdir(build_temp)) def test_suite(): return unittest.makeSuite(BuildCLibTestCase) diff --git a/Lib/distutils/tests/test_build_ext.py b/Lib/distutils/tests/test_build_ext.py index 44a9852f4c..3cfaa2c692 100644 --- a/Lib/distutils/tests/test_build_ext.py +++ b/Lib/distutils/tests/test_build_ext.py @@ -76,8 +76,8 @@ class BuildExtTestCase(TempdirManager, if 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)) + self.assertIsInstance(xx.Null(), xx.Null) + self.assertIsInstance(xx.Str(), xx.Str) def tearDown(self): # Get everything back to normal @@ -110,7 +110,7 @@ class BuildExtTestCase(TempdirManager, _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris - self.assertTrue(len(cmd.library_dirs) > 0) + self.assertGreater(len(cmd.library_dirs), 0) def test_user_site(self): # site.USER_SITE was introduced in 2.6 @@ -124,7 +124,7 @@ class BuildExtTestCase(TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assertTrue('user' in options) + self.assertIn('user', options) # setting a value cmd.user = 1 @@ -171,10 +171,10 @@ class BuildExtTestCase(TempdirManager, from distutils import sysconfig py_include = sysconfig.get_python_inc() - self.assertTrue(py_include in cmd.include_dirs) + self.assertIn(py_include, cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) - self.assertTrue(plat_py_include in cmd.include_dirs) + self.assertIn(plat_py_include, cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string @@ -255,13 +255,13 @@ class BuildExtTestCase(TempdirManager, 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] - self.assertTrue(isinstance(ext, Extension)) + self.assertIsInstance(ext, Extension) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') self.assertEqual(ext.libraries, 'foo') - self.assertTrue(not hasattr(ext, 'some')) + self.assertFalse(hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', diff --git a/Lib/distutils/tests/test_build_py.py b/Lib/distutils/tests/test_build_py.py index e416edd4a1..2ce9d4492d 100644 --- a/Lib/distutils/tests/test_build_py.py +++ b/Lib/distutils/tests/test_build_py.py @@ -121,6 +121,37 @@ class BuildPyTestCase(support.TempdirManager, found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) self.assertEqual(sorted(found), ['boiledeggs.%s.pyo' % imp.get_tag()]) + def test_dir_in_package_data(self): + """ + A directory in package_data should not be added to the filelist. + """ + # See bug 19286 + sources = self.mkdtemp() + pkg_dir = os.path.join(sources, "pkg") + + os.mkdir(pkg_dir) + open(os.path.join(pkg_dir, "__init__.py"), "w").close() + + docdir = os.path.join(pkg_dir, "doc") + os.mkdir(docdir) + open(os.path.join(docdir, "testfile"), "w").close() + + # create the directory that could be incorrectly detected as a file + os.mkdir(os.path.join(docdir, 'otherdir')) + + os.chdir(sources) + dist = Distribution({"packages": ["pkg"], + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data when data dir includes a dir") + def test_dont_write_bytecode(self): # makes sure byte_compile is not used dist = self.create_dist()[1] diff --git a/Lib/distutils/tests/test_build_scripts.py b/Lib/distutils/tests/test_build_scripts.py index e3326b8517..954fc76398 100644 --- a/Lib/distutils/tests/test_build_scripts.py +++ b/Lib/distutils/tests/test_build_scripts.py @@ -17,8 +17,8 @@ class BuildScriptsTestCase(support.TempdirManager, def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) - self.assertTrue(not cmd.force) - self.assertTrue(cmd.build_dir is None) + self.assertFalse(cmd.force) + self.assertIsNone(cmd.build_dir) cmd.finalize_options() @@ -38,7 +38,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assertTrue(name in built) + self.assertIn(name, built) def get_build_scripts_cmd(self, target, scripts): import sys @@ -103,7 +103,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assertTrue(name in built) + self.assertIn(name, built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) diff --git a/Lib/distutils/tests/test_clean.py b/Lib/distutils/tests/test_clean.py index eb8958bff5..b64f300a04 100755..100644 --- a/Lib/distutils/tests/test_clean.py +++ b/Lib/distutils/tests/test_clean.py @@ -36,7 +36,7 @@ class cleanTestCase(support.TempdirManager, # make sure the files where removed for name, path in dirs: - self.assertTrue(not os.path.exists(path), + self.assertFalse(os.path.exists(path), '%s was not removed' % path) # let's run the command again (should spit warnings but succeed) diff --git a/Lib/distutils/tests/test_config.py b/Lib/distutils/tests/test_config.py index 525bee9416..0e8d65e271 100644 --- a/Lib/distutils/tests/test_config.py +++ b/Lib/distutils/tests/test_config.py @@ -103,7 +103,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, def test_server_empty_registration(self): cmd = self._cmd(self.dist) rc = cmd._get_rc_file() - self.assertTrue(not os.path.exists(rc)) + self.assertFalse(os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) f = open(rc) diff --git a/Lib/distutils/tests/test_config_cmd.py b/Lib/distutils/tests/test_config_cmd.py index e2e6e4ebaa..79894c78d3 100644 --- a/Lib/distutils/tests/test_config_cmd.py +++ b/Lib/distutils/tests/test_config_cmd.py @@ -81,7 +81,7 @@ class ConfigTestCase(support.LoggingSilencer, cmd._clean(f1, f2) for f in (f1, f2): - self.assertTrue(not os.path.exists(f)) + self.assertFalse(os.path.exists(f)) def test_suite(): return unittest.makeSuite(ConfigTestCase) diff --git a/Lib/distutils/tests/test_dist.py b/Lib/distutils/tests/test_dist.py index 66c20e27e2..9a8ca19902 100644 --- a/Lib/distutils/tests/test_dist.py +++ b/Lib/distutils/tests/test_dist.py @@ -8,7 +8,7 @@ import textwrap from unittest import mock -from distutils.dist import Distribution, fix_help_options +from distutils.dist import Distribution, fix_help_options, DistributionMetadata from distutils.cmd import Command from test.support import TESTFN, captured_stdout, run_unittest @@ -388,6 +388,33 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertTrue(output) + def test_read_metadata(self): + attrs = {"name": "package", + "version": "1.0", + "long_description": "desc", + "description": "xxx", + "download_url": "http://example.com", + "keywords": ['one', 'two'], + "requires": ['foo']} + + dist = Distribution(attrs) + metadata = dist.metadata + + # write it then reloads it + PKG_INFO = io.StringIO() + metadata.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + metadata.read_pkg_file(PKG_INFO) + + self.assertEquals(metadata.name, "package") + self.assertEquals(metadata.version, "1.0") + self.assertEquals(metadata.description, "xxx") + self.assertEquals(metadata.download_url, 'http://example.com') + self.assertEquals(metadata.keywords, ['one', 'two']) + self.assertEquals(metadata.platforms, ['UNKNOWN']) + self.assertEquals(metadata.obsoletes, None) + self.assertEquals(metadata.requires, ['foo']) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) diff --git a/Lib/distutils/tests/test_install.py b/Lib/distutils/tests/test_install.py index b1901273e9..ede88e5d90 100644 --- a/Lib/distutils/tests/test_install.py +++ b/Lib/distutils/tests/test_install.py @@ -236,7 +236,7 @@ class InstallTestCase(support.TempdirManager, self.test_record() finally: install_module.DEBUG = False - self.assertTrue(len(self.logs) > old_logs_len) + self.assertGreater(len(self.logs), old_logs_len) def test_suite(): diff --git a/Lib/distutils/tests/test_install_lib.py b/Lib/distutils/tests/test_install_lib.py index 2bd4dc6e96..d0dfca00d7 100644 --- a/Lib/distutils/tests/test_install_lib.py +++ b/Lib/distutils/tests/test_install_lib.py @@ -103,7 +103,7 @@ class InstallLibTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + self.assertIn('byte-compiling is disabled', self.logs[0][1]) def test_suite(): diff --git a/Lib/distutils/tests/test_install_scripts.py b/Lib/distutils/tests/test_install_scripts.py index 8952e744e5..1f7b1038cb 100644 --- a/Lib/distutils/tests/test_install_scripts.py +++ b/Lib/distutils/tests/test_install_scripts.py @@ -24,10 +24,10 @@ class InstallScriptsTestCase(support.TempdirManager, skip_build=1, ) cmd = install_scripts(dist) - self.assertTrue(not cmd.force) - self.assertTrue(not cmd.skip_build) - self.assertTrue(cmd.build_dir is None) - self.assertTrue(cmd.install_dir is None) + self.assertFalse(cmd.force) + self.assertFalse(cmd.skip_build) + self.assertIsNone(cmd.build_dir) + self.assertIsNone(cmd.install_dir) cmd.finalize_options() @@ -72,7 +72,7 @@ class InstallScriptsTestCase(support.TempdirManager, installed = os.listdir(target) for name in expected: - self.assertTrue(name in installed) + self.assertIn(name, installed) def test_suite(): diff --git a/Lib/distutils/tests/test_msvc9compiler.py b/Lib/distutils/tests/test_msvc9compiler.py index 301d43d20c..5e18c61360 100644 --- a/Lib/distutils/tests/test_msvc9compiler.py +++ b/Lib/distutils/tests/test_msvc9compiler.py @@ -128,7 +128,7 @@ class msvc9compilerTestCase(support.TempdirManager, # windows registeries versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') - self.assertTrue(v in ('0', '1', '2')) + self.assertIn(v, ('0', '1', '2')) import winreg HKCU = winreg.HKEY_CURRENT_USER @@ -136,7 +136,7 @@ class msvc9compilerTestCase(support.TempdirManager, self.assertEqual(keys, None) keys = Reg.read_keys(HKCU, r'Control Panel') - self.assertTrue('Desktop' in keys) + self.assertIn('Desktop', keys) def test_remove_visual_c_ref(self): from distutils.msvc9compiler import MSVCCompiler @@ -174,7 +174,7 @@ class msvc9compilerTestCase(support.TempdirManager, compiler = MSVCCompiler() got = compiler._remove_visual_c_ref(manifest) - self.assertIs(got, None) + self.assertIsNone(got) def test_suite(): diff --git a/Lib/distutils/tests/test_register.py b/Lib/distutils/tests/test_register.py index a86b8606e4..f4efa13d78 100644 --- a/Lib/distutils/tests/test_register.py +++ b/Lib/distutils/tests/test_register.py @@ -98,7 +98,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd = self._get_cmd() # we shouldn't have a .pypirc file yet - self.assertTrue(not os.path.exists(self.rc)) + self.assertFalse(os.path.exists(self.rc)) # patching input and getpass.getpass # so register gets happy @@ -145,7 +145,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertEqual(req1['Content-length'], '1374') self.assertEqual(req2['Content-length'], '1374') - self.assertTrue((b'xxx') in self.conn.reqs[1].data) + self.assertIn(b'xxx', self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -175,7 +175,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '608') - self.assertTrue((b'tarek') in req.data) + self.assertIn(b'tarek', req.data) def test_password_reset(self): # this test runs choice 3 @@ -193,7 +193,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '290') - self.assertTrue((b'tarek') in req.data) + self.assertIn(b'tarek', req.data) @unittest.skipUnless(docutils is not None, 'needs docutils') def test_strict(self): diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py index 826ea4247d..07812d80fc 100644 --- a/Lib/distutils/tests/test_sysconfig.py +++ b/Lib/distutils/tests/test_sysconfig.py @@ -50,7 +50,7 @@ class SysconfigTestCase(support.EnvironGuard, def test_get_config_vars(self): cvars = sysconfig.get_config_vars() - self.assertTrue(isinstance(cvars, dict)) + self.assertIsInstance(cvars, dict) self.assertTrue(cvars) def test_srcdir(self): diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py index 4c6464a32e..a474596e33 100644 --- a/Lib/distutils/tests/test_upload.py +++ b/Lib/distutils/tests/test_upload.py @@ -1,9 +1,9 @@ """Tests for distutils.command.upload.""" import os import unittest -import http.client as httpclient from test.support import run_unittest +from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution @@ -37,48 +37,37 @@ index-servers = [server1] username:me """ -class Response(object): - def __init__(self, status=200, reason='OK'): - self.status = status - self.reason = reason -class FakeConnection(object): +class FakeOpen(object): - def __init__(self): - self.requests = [] - self.headers = [] - self.body = '' + def __init__(self, url): + self.url = url + if not isinstance(url, str): + self.req = url + else: + self.req = None + self.msg = 'OK' - def __call__(self, netloc): - return self + def getcode(self): + return 200 - def connect(self): - pass - endheaders = connect - - def putrequest(self, method, url): - self.requests.append((method, url)) - - def putheader(self, name, value): - self.headers.append((name, value)) - - def send(self, body): - self.body = body - - def getresponse(self): - return Response() class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_class = httpclient.HTTPConnection - self.conn = httpclient.HTTPConnection = FakeConnection() + self.old_open = upload_mod.urlopen + upload_mod.urlopen = self._urlopen + self.last_open = None def tearDown(self): - httpclient.HTTPConnection = self.old_class + upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() + def _urlopen(self, url): + self.last_open = FakeOpen(url) + return self.last_open + def test_finalize_options(self): # new format @@ -122,14 +111,14 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.ensure_finalized() cmd.run() - # what did we send ? - headers = dict(self.conn.headers) + # what did we send ? + headers = dict(self.last_open.req.headers) self.assertEqual(headers['Content-length'], '2087') - self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) - self.assertFalse('\n' in headers['Authorization']) - - self.assertEqual(self.conn.requests, [('POST', '/pypi')]) - self.assertTrue((b'xxx') in self.conn.body) + self.assert_(headers['Content-type'].startswith('multipart/form-data')) + self.assertEquals(self.last_open.req.get_method(), 'POST') + self.assertEquals(self.last_open.req.get_full_url(), + 'http://pypi.python.org/pypi') + self.assert_(b'xxx' in self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/Lib/distutils/tests/test_util.py b/Lib/distutils/tests/test_util.py index eac9b5141d..00219cfdba 100644 --- a/Lib/distutils/tests/test_util.py +++ b/Lib/distutils/tests/test_util.py @@ -266,7 +266,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertTrue(strtobool(y)) for n in no: - self.assertTrue(not strtobool(n)) + self.assertFalse(strtobool(n)) def test_rfc822_escape(self): header = 'I am a\npoor\nlonesome\nheader\n' diff --git a/Lib/email/message.py b/Lib/email/message.py index 5020a0325e..f43a3809be 100644 --- a/Lib/email/message.py +++ b/Lib/email/message.py @@ -636,7 +636,7 @@ class Message: If your application doesn't care whether the parameter was RFC 2231 encoded, it can turn the return value into a string as follows: - param = msg.get_param('foo') + rawparam = msg.get_param('foo') param = email.utils.collapse_rfc2231_value(rawparam) """ diff --git a/Lib/html/parser.py b/Lib/html/parser.py index 60a322a949..2d3bef351b 100644 --- a/Lib/html/parser.py +++ b/Lib/html/parser.py @@ -23,16 +23,16 @@ charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]') starttagopen = re.compile('<[a-zA-Z]') piclose = re.compile('>') commentclose = re.compile(r'--\s*>') -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]*') # Note: # 1) the strict attrfind isn't really strict, but we can't make it # correctly strict without breaking backward compatibility; -# 2) if you change attrfind remember to update locatestarttagend too; -# 3) if you change attrfind and/or locatestarttagend the parser will +# 2) if you change tagfind/attrfind remember to update locatestarttagend too; +# 3) if you change tagfind/attrfind and/or locatestarttagend the parser will # explode, so don't do it. +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]*)(?:\s|/(?!>))*') attrfind = re.compile( r'\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*' r'(\'[^\']*\'|"[^"]*"|[^\s"\'=<>`]*))?') @@ -54,7 +54,7 @@ locatestarttagend = re.compile(r""" \s* # trailing whitespace """, re.VERBOSE) locatestarttagend_tolerant = re.compile(r""" - <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name + <[a-zA-Z][^\t\n\r\f />\x00]* # tag name (?:[\s/]* # optional whitespace before attribute name (?:(?<=['"\s/])[^\s/>][^\s/=>]* # attribute name (?:\s*=+\s* # value indicator @@ -328,7 +328,10 @@ class HTMLParser(_markupbase.ParserBase): # Now parse the data between i+1 and j into a tag and attrs attrs = [] - match = tagfind.match(rawdata, i+1) + if self.strict: + match = tagfind.match(rawdata, i+1) + else: + match = tagfind_tolerant.match(rawdata, i+1) assert match, 'unexpected call to parse_starttag()' k = match.end() self.lasttag = tag = match.group(1).lower() @@ -440,7 +443,7 @@ class HTMLParser(_markupbase.ParserBase): return i+3 else: return self.parse_bogus_comment(i) - tagname = namematch.group().lower() + tagname = namematch.group(1).lower() # consume and ignore other stuff between the name and the > # Note: this is not 100% correct, since we might have things like # </tag attr=">">, but looking for > after tha name should cover diff --git a/Lib/lib2to3/tests/test_fixers.py b/Lib/lib2to3/tests/test_fixers.py index 2464446f40..2f08f935d9 100644 --- a/Lib/lib2to3/tests/test_fixers.py +++ b/Lib/lib2to3/tests/test_fixers.py @@ -41,7 +41,7 @@ class FixerTestCase(support.TestCase): def warns(self, before, after, message, unchanged=False): tree = self._check(before, after) - self.assertTrue(message in "".join(self.fixer_log)) + self.assertIn(message, "".join(self.fixer_log)) if not unchanged: self.assertTrue(tree.was_changed) diff --git a/Lib/lib2to3/tests/test_main.py b/Lib/lib2to3/tests/test_main.py index a498c5a0d1..a33c45c50a 100644 --- a/Lib/lib2to3/tests/test_main.py +++ b/Lib/lib2to3/tests/test_main.py @@ -49,9 +49,9 @@ class TestMain(unittest.TestCase): ret = self.run_2to3_capture(["-"], input_stream, out_enc, err) self.assertEqual(ret, 0) output = out.getvalue().decode("ascii") - self.assertTrue("-print 'nothing'" in output) - self.assertTrue("WARNING: couldn't encode <stdin>'s diff for " - "your terminal" in err.getvalue()) + self.assertIn("-print 'nothing'", output) + self.assertIn("WARNING: couldn't encode <stdin>'s diff for " + "your terminal", err.getvalue()) def setup_test_source_trees(self): """Setup a test source tree and output destination tree.""" diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py index 09b439a07a..a383a14e30 100644 --- a/Lib/lib2to3/tests/test_parser.py +++ b/Lib/lib2to3/tests/test_parser.py @@ -168,8 +168,8 @@ class TestParserIdempotency(support.TestCase): for filepath in support.all_project_files(): with open(filepath, "rb") as fp: encoding = tokenize.detect_encoding(fp.readline)[0] - self.assertTrue(encoding is not None, - "can't detect encoding for %s" % filepath) + self.assertIsNotNone(encoding, + "can't detect encoding for %s" % filepath) with open(filepath, "r", encoding=encoding) as fp: source = fp.read() try: diff --git a/Lib/lib2to3/tests/test_pytree.py b/Lib/lib2to3/tests/test_pytree.py index a2ab1f3534..4d585a8841 100644 --- a/Lib/lib2to3/tests/test_pytree.py +++ b/Lib/lib2to3/tests/test_pytree.py @@ -143,12 +143,12 @@ class TestNodes(support.TestCase): l3 = pytree.Leaf(100, "bar") n1 = pytree.Node(1000, [l1, l2, l3]) self.assertEqual(n1.children, [l1, l2, l3]) - self.assertTrue(isinstance(n1.children, list)) + self.assertIsInstance(n1.children, list) self.assertFalse(n1.was_changed) l2new = pytree.Leaf(100, "-") l2.replace(l2new) self.assertEqual(n1.children, [l1, l2new, l3]) - self.assertTrue(isinstance(n1.children, list)) + self.assertIsInstance(n1.children, list) self.assertTrue(n1.was_changed) def test_replace_with_list(self): @@ -159,7 +159,7 @@ class TestNodes(support.TestCase): l2.replace([pytree.Leaf(100, "*"), pytree.Leaf(100, "*")]) self.assertEqual(str(n1), "foo**bar") - self.assertTrue(isinstance(n1.children, list)) + self.assertIsInstance(n1.children, list) def test_leaves(self): l1 = pytree.Leaf(100, "foo") @@ -330,7 +330,7 @@ class TestNodes(support.TestCase): n2 = pytree.Node(1000, []) p1 = pytree.Node(1000, [n1, n2]) - self.assertTrue(n1.next_sibling is n2) + self.assertIs(n1.next_sibling, n2) self.assertEqual(n2.next_sibling, None) self.assertEqual(p1.next_sibling, None) @@ -339,7 +339,7 @@ class TestNodes(support.TestCase): l2 = pytree.Leaf(100, "b") p1 = pytree.Node(1000, [l1, l2]) - self.assertTrue(l1.next_sibling is l2) + self.assertIs(l1.next_sibling, l2) self.assertEqual(l2.next_sibling, None) self.assertEqual(p1.next_sibling, None) @@ -348,7 +348,7 @@ class TestNodes(support.TestCase): n2 = pytree.Node(1000, []) p1 = pytree.Node(1000, [n1, n2]) - self.assertTrue(n2.prev_sibling is n1) + self.assertIs(n2.prev_sibling, n1) self.assertEqual(n1.prev_sibling, None) self.assertEqual(p1.prev_sibling, None) @@ -357,7 +357,7 @@ class TestNodes(support.TestCase): l2 = pytree.Leaf(100, "b") p1 = pytree.Node(1000, [l1, l2]) - self.assertTrue(l2.prev_sibling is l1) + self.assertIs(l2.prev_sibling, l1) self.assertEqual(l1.prev_sibling, None) self.assertEqual(p1.prev_sibling, None) @@ -430,7 +430,7 @@ class TestPatterns(support.TestCase): r = {} self.assertTrue(pw.match_seq([l1, l3], r)) self.assertEqual(r, {"pl": l3, "pw": [l1, l3]}) - self.assertTrue(r["pl"] is l3) + self.assertIs(r["pl"], l3) r = {} def test_generate_matches(self): diff --git a/Lib/lib2to3/tests/test_refactor.py b/Lib/lib2to3/tests/test_refactor.py index 5ecd9b1cb3..f30c1e8630 100644 --- a/Lib/lib2to3/tests/test_refactor.py +++ b/Lib/lib2to3/tests/test_refactor.py @@ -49,9 +49,9 @@ class TestRefactoringTool(unittest.TestCase): def test_print_function_option(self): rt = self.rt({"print_function" : True}) - self.assertTrue(rt.grammar is pygram.python_grammar_no_print_statement) - self.assertTrue(rt.driver.grammar is - pygram.python_grammar_no_print_statement) + self.assertIs(rt.grammar, pygram.python_grammar_no_print_statement) + self.assertIs(rt.driver.grammar, + pygram.python_grammar_no_print_statement) def test_write_unchanged_files_option(self): rt = self.rt() diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index 4cef66fa13..7f94e39f0f 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -976,8 +976,10 @@ class FileHandler(StreamHandler): self.flush() if hasattr(self.stream, "close"): self.stream.close() - StreamHandler.close(self) self.stream = None + # Issue #19523: call unconditionally to + # prevent a handler leak when delay is set + StreamHandler.close(self) finally: self.release() diff --git a/Lib/multiprocessing/pool.py b/Lib/multiprocessing/pool.py index fc9d90402b..0f2dab48eb 100644 --- a/Lib/multiprocessing/pool.py +++ b/Lib/multiprocessing/pool.py @@ -147,7 +147,8 @@ class Pool(object): self._task_handler = threading.Thread( target=Pool._handle_tasks, - args=(self._taskqueue, self._quick_put, self._outqueue, self._pool) + args=(self._taskqueue, self._quick_put, self._outqueue, + self._pool, self._cache) ) self._task_handler.daemon = True self._task_handler._state = RUN @@ -338,7 +339,7 @@ class Pool(object): debug('worker handler exiting') @staticmethod - def _handle_tasks(taskqueue, put, outqueue, pool): + def _handle_tasks(taskqueue, put, outqueue, pool, cache): thread = threading.current_thread() for taskseq, set_length in iter(taskqueue.get, None): @@ -349,9 +350,12 @@ class Pool(object): break try: put(task) - except IOError: - debug('could not put task on queue') - break + except Exception as e: + job, ind = task[:2] + try: + cache[job]._set(ind, (False, e)) + except KeyError: + pass else: if set_length: debug('doing set_length()') diff --git a/Lib/subprocess.py b/Lib/subprocess.py index 376b50546b..c3a278836c 100644 --- a/Lib/subprocess.py +++ b/Lib/subprocess.py @@ -662,7 +662,6 @@ def list2cmdline(seq): # Various tools for executing commands and looking at their output and status. # -# NB This only works (and is only relevant) for POSIX. def getstatusoutput(cmd): """Return (status, output) of executing cmd in a shell. @@ -681,21 +680,15 @@ def getstatusoutput(cmd): >>> subprocess.getstatusoutput('/bin/junk') (256, 'sh: /bin/junk: not found') """ - with os.popen('{ ' + cmd + '; } 2>&1', 'r') as pipe: - try: - text = pipe.read() - sts = pipe.close() - except: - process = pipe._proc - process.kill() - process.wait() - raise - if sts is None: - sts = 0 - if text[-1:] == '\n': - text = text[:-1] - return sts, text - + try: + data = check_output(cmd, shell=True, universal_newlines=True, stderr=STDOUT) + status = 0 + except CalledProcessError as ex: + data = ex.output + status = ex.returncode + if data[-1:] == '\n': + data = data[:-1] + return status, data def getoutput(cmd): """Return output (stdout or stderr) of executing cmd in a shell. diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index f21b69f367..e57ff24774 100755 --- a/Lib/test/test_array.py +++ b/Lib/test/test_array.py @@ -11,6 +11,7 @@ import operator import io import math import struct +import sys import warnings import array @@ -993,15 +994,15 @@ class BaseTest: s = None self.assertRaises(ReferenceError, len, p) + @unittest.skipUnless(hasattr(sys, 'getrefcount'), + 'test needs sys.getrefcount()') def test_bug_782369(self): - import sys - if hasattr(sys, "getrefcount"): - for i in range(10): - b = array.array('B', range(64)) - rc = sys.getrefcount(10) - for i in range(10): - b = array.array('B', range(64)) - self.assertEqual(rc, sys.getrefcount(10)) + for i in range(10): + b = array.array('B', range(64)) + rc = sys.getrefcount(10) + for i in range(10): + b = array.array('B', range(64)) + self.assertEqual(rc, sys.getrefcount(10)) def test_subclass_with_kwargs(self): # SF bug #1486663 -- this used to erroneously raise a TypeError diff --git a/Lib/test/test_bz2.py b/Lib/test/test_bz2.py index 912fac1c33..6764e59553 100644 --- a/Lib/test/test_bz2.py +++ b/Lib/test/test_bz2.py @@ -5,6 +5,7 @@ from test.support import TESTFN, bigmemtest, _4G import unittest from io import BytesIO import os +import pickle import random import subprocess import sys @@ -621,6 +622,11 @@ class BZ2CompressorTest(BaseTest): finally: data = None + def testPickle(self): + with self.assertRaises(TypeError): + pickle.dumps(BZ2Compressor()) + + class BZ2DecompressorTest(BaseTest): def test_Constructor(self): self.assertRaises(TypeError, BZ2Decompressor, 42) @@ -672,6 +678,10 @@ class BZ2DecompressorTest(BaseTest): compressed = None decompressed = None + def testPickle(self): + with self.assertRaises(TypeError): + pickle.dumps(BZ2Decompressor()) + class CompressDecompressTest(BaseTest): def testCompress(self): diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index f1ea5a9fde..9013a7b188 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -1,7 +1,6 @@ # Run the _testcapi module tests (tests for the Python/C API): by defn, # these are all functions _testcapi exports whose name begins with 'test_'. -from __future__ import with_statement import os import pickle import random @@ -351,17 +350,12 @@ class TestThreadState(unittest.TestCase): t.start() t.join() - -def test_main(): - support.run_unittest(CAPITest, TestPendingCalls, Test6012, - EmbeddingTest, SkipitemTest, TestThreadState) - - for name in dir(_testcapi): - if name.startswith('test_'): - test = getattr(_testcapi, name) - if support.verbose: - print("internal", name) - test() +class Test_testcapi(unittest.TestCase): + def test__testcapi(self): + for name in dir(_testcapi): + if name.startswith('test_'): + test = getattr(_testcapi, name) + test() if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py index ba9fe465f8..fddb538efb 100644 --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -39,11 +39,10 @@ class CompileallTests(unittest.TestCase): compare = struct.pack('<4sl', imp.get_magic(), mtime) return data, compare + @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()') def recreation_check(self, metadata): """Check that compileall recreates bytecode when the new metadata is used.""" - if not hasattr(os, 'stat'): - return py_compile.compile(self.source_path) self.assertEqual(*self.data()) with open(self.bc_path, 'rb') as file: diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 5e285865a7..976e6205b7 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -896,78 +896,77 @@ Stonecutters Seafood and Chop House+ Lemont+ IL+ 12/19/02+ Week Back dialect = sniffer.sniff(self.sample9) self.assertTrue(dialect.doublequote) -if not hasattr(sys, "gettotalrefcount"): - if 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 range(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 range(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 range(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 range(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) +class NUL: + def write(s, *args): + pass + writelines = write + +@unittest.skipUnless(hasattr(sys, "gettotalrefcount"), + 'requires sys.gettotalrefcount()') +class TestLeaks(unittest.TestCase): + def test_create_read(self): + delta = 0 + lastrc = sys.gettotalrefcount() + for i in range(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 range(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 range(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 range(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) class TestUnicode(unittest.TestCase): diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py index e23392959a..208bc4c382 100644 --- a/Lib/test/test_dbm_dumb.py +++ b/Lib/test/test_dbm_dumb.py @@ -37,11 +37,9 @@ class DumbDBMTestCase(unittest.TestCase): self.read_helper(f) f.close() + @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()') + @unittest.skipUnless(hasattr(os, 'chmod'), 'test needs os.chmod()') def test_dumbdbm_creation_mode(self): - # On platforms without chmod, don't do anything. - if not (hasattr(os, 'chmod') and hasattr(os, 'umask')): - return - try: old_umask = os.umask(0o002) f = dumbdbm.open(_fname, 'c', 0o637) diff --git a/Lib/test/test_email/test_defect_handling.py b/Lib/test/test_email/test_defect_handling.py index 305432df37..f36b907573 100644 --- a/Lib/test/test_email/test_defect_handling.py +++ b/Lib/test/test_email/test_defect_handling.py @@ -59,8 +59,8 @@ class TestDefectsBase: inner = msg.get_payload(0) self.assertTrue(hasattr(inner, 'defects')) self.assertEqual(len(self.get_defects(inner)), 1) - self.assertTrue(isinstance(self.get_defects(inner)[0], - errors.StartBoundaryNotFoundDefect)) + self.assertIsInstance(self.get_defects(inner)[0], + errors.StartBoundaryNotFoundDefect) def test_multipart_no_boundary(self): source = textwrap.dedent("""\ @@ -84,12 +84,12 @@ class TestDefectsBase: with self._raise_point(errors.NoBoundaryInMultipartDefect): msg = self._str_msg(source) if self.raise_expected: return - self.assertTrue(isinstance(msg.get_payload(), str)) + self.assertIsInstance(msg.get_payload(), str) self.assertEqual(len(self.get_defects(msg)), 2) - self.assertTrue(isinstance(self.get_defects(msg)[0], - errors.NoBoundaryInMultipartDefect)) - self.assertTrue(isinstance(self.get_defects(msg)[1], - errors.MultipartInvariantViolationDefect)) + self.assertIsInstance(self.get_defects(msg)[0], + errors.NoBoundaryInMultipartDefect) + self.assertIsInstance(self.get_defects(msg)[1], + errors.MultipartInvariantViolationDefect) multipart_msg = textwrap.dedent("""\ Date: Wed, 14 Nov 2007 12:56:23 GMT @@ -153,10 +153,10 @@ class TestDefectsBase: if self.raise_expected: return self.assertTrue(hasattr(msg, 'defects')) self.assertEqual(len(self.get_defects(msg)), 2) - self.assertTrue(isinstance(self.get_defects(msg)[0], - errors.NoBoundaryInMultipartDefect)) - self.assertTrue(isinstance(self.get_defects(msg)[1], - errors.MultipartInvariantViolationDefect)) + self.assertIsInstance(self.get_defects(msg)[0], + errors.NoBoundaryInMultipartDefect) + self.assertIsInstance(self.get_defects(msg)[1], + errors.MultipartInvariantViolationDefect) def test_missing_start_boundary(self): source = textwrap.dedent("""\ @@ -193,8 +193,8 @@ class TestDefectsBase: if self.raise_expected: return bad = outer.get_payload(1).get_payload(0) self.assertEqual(len(self.get_defects(bad)), 1) - self.assertTrue(isinstance(self.get_defects(bad)[0], - errors.StartBoundaryNotFoundDefect)) + self.assertIsInstance(self.get_defects(bad)[0], + errors.StartBoundaryNotFoundDefect) def test_first_line_is_continuation_header(self): with self._raise_point(errors.FirstHeaderLineIsContinuationDefect): diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 904c06a18c..cd4f757c1f 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -241,12 +241,12 @@ class TestMessageAPI(TestEmailBase): msg['From'] = 'Me' msg['to'] = 'You' # Check for case insensitivity - self.assertTrue('from' in msg) - self.assertTrue('From' in msg) - self.assertTrue('FROM' in msg) - self.assertTrue('to' in msg) - self.assertTrue('To' in msg) - self.assertTrue('TO' in msg) + self.assertIn('from', msg) + self.assertIn('From', msg) + self.assertIn('FROM', msg) + self.assertIn('to', msg) + self.assertIn('To', msg) + self.assertIn('TO', msg) def test_as_string(self): eq = self.ndiffAssertEqual @@ -339,12 +339,11 @@ class TestMessageAPI(TestEmailBase): self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') def test_field_containment(self): - unless = self.assertTrue msg = email.message_from_string('Header: exists') - unless('header' in msg) - unless('Header' in msg) - unless('HEADER' in msg) - self.assertFalse('headerx' in msg) + self.assertIn('header', msg) + self.assertIn('Header', msg) + self.assertIn('HEADER', msg) + self.assertNotIn('headerx', msg) def test_set_param(self): eq = self.assertEqual @@ -1400,7 +1399,6 @@ class TestMIMEAudio(unittest.TestCase): def test_add_header(self): eq = self.assertEqual - unless = self.assertTrue self._au.add_header('Content-Disposition', 'attachment', filename='audiotest.au') eq(self._au['content-disposition'], @@ -1411,12 +1409,12 @@ class TestMIMEAudio(unittest.TestCase): 'audiotest.au') missing = [] eq(self._au.get_param('attachment', header='content-disposition'), '') - unless(self._au.get_param('foo', failobj=missing, - header='content-disposition') is missing) + self.assertIs(self._au.get_param('foo', failobj=missing, + header='content-disposition'), missing) # Try some missing stuff - unless(self._au.get_param('foobar', missing) is missing) - unless(self._au.get_param('attachment', missing, - header='foobar') is missing) + self.assertIs(self._au.get_param('foobar', missing), missing) + self.assertIs(self._au.get_param('attachment', missing, + header='foobar'), missing) @@ -1441,7 +1439,6 @@ class TestMIMEImage(unittest.TestCase): def test_add_header(self): eq = self.assertEqual - unless = self.assertTrue self._im.add_header('Content-Disposition', 'attachment', filename='dingusfish.gif') eq(self._im['content-disposition'], @@ -1452,12 +1449,12 @@ class TestMIMEImage(unittest.TestCase): 'dingusfish.gif') missing = [] eq(self._im.get_param('attachment', header='content-disposition'), '') - unless(self._im.get_param('foo', failobj=missing, - header='content-disposition') is missing) + self.assertIs(self._im.get_param('foo', failobj=missing, + header='content-disposition'), missing) # Try some missing stuff - unless(self._im.get_param('foobar', missing) is missing) - unless(self._im.get_param('attachment', missing, - header='foobar') is missing) + self.assertIs(self._im.get_param('foobar', missing), missing) + self.assertIs(self._im.get_param('attachment', missing, + header='foobar'), missing) @@ -1548,17 +1545,16 @@ class TestMIMEText(unittest.TestCase): def test_types(self): eq = self.assertEqual - unless = self.assertTrue eq(self._msg.get_content_type(), 'text/plain') eq(self._msg.get_param('charset'), 'us-ascii') missing = [] - unless(self._msg.get_param('foobar', missing) is missing) - unless(self._msg.get_param('charset', missing, header='foobar') - is missing) + self.assertIs(self._msg.get_param('foobar', missing), missing) + self.assertIs(self._msg.get_param('charset', missing, header='foobar'), + missing) def test_payload(self): self.assertEqual(self._msg.get_payload(), 'hello there') - self.assertTrue(not self._msg.is_multipart()) + self.assertFalse(self._msg.is_multipart()) def test_charset(self): eq = self.assertEqual @@ -1577,7 +1573,7 @@ class TestMIMEText(unittest.TestCase): msg = MIMEText('hello there') eq(msg.get_charset(), 'us-ascii') eq(msg['content-type'], 'text/plain; charset="us-ascii"') - self.assertTrue('hello there' in msg.as_string()) + self.assertIn('hello there', msg.as_string()) def test_utf8_input(self): teststr = '\u043a\u0438\u0440\u0438\u043b\u0438\u0446\u0430' @@ -1636,21 +1632,20 @@ This is the dingus fish. def test_hierarchy(self): # convenience eq = self.assertEqual - unless = self.assertTrue raises = self.assertRaises # tests m = self._msg - unless(m.is_multipart()) + self.assertTrue(m.is_multipart()) eq(m.get_content_type(), 'multipart/mixed') eq(len(m.get_payload()), 2) raises(IndexError, m.get_payload, 2) m0 = m.get_payload(0) m1 = m.get_payload(1) - unless(m0 is self._txt) - unless(m1 is self._im) + self.assertIs(m0, self._txt) + self.assertIs(m1, self._im) eq(m.get_payload(), [m0, m1]) - unless(not m0.is_multipart()) - unless(not m1.is_multipart()) + self.assertFalse(m0.is_multipart()) + self.assertFalse(m1.is_multipart()) def test_empty_multipart_idempotent(self): text = """\ @@ -1982,25 +1977,23 @@ class TestNonConformant(TestEmailBase): # test_defect_handling def test_same_boundary_inner_outer(self): - unless = self.assertTrue msg = self._msgobj('msg_15.txt') # XXX We can probably eventually do better inner = msg.get_payload(0) - unless(hasattr(inner, 'defects')) + self.assertTrue(hasattr(inner, 'defects')) self.assertEqual(len(inner.defects), 1) - unless(isinstance(inner.defects[0], - errors.StartBoundaryNotFoundDefect)) + self.assertIsInstance(inner.defects[0], + errors.StartBoundaryNotFoundDefect) # test_defect_handling def test_multipart_no_boundary(self): - unless = self.assertTrue msg = self._msgobj('msg_25.txt') - unless(isinstance(msg.get_payload(), str)) + self.assertIsInstance(msg.get_payload(), str) self.assertEqual(len(msg.defects), 2) - unless(isinstance(msg.defects[0], - errors.NoBoundaryInMultipartDefect)) - unless(isinstance(msg.defects[1], - errors.MultipartInvariantViolationDefect)) + self.assertIsInstance(msg.defects[0], + errors.NoBoundaryInMultipartDefect) + self.assertIsInstance(msg.defects[1], + errors.MultipartInvariantViolationDefect) multipart_msg = textwrap.dedent("""\ Date: Wed, 14 Nov 2007 12:56:23 GMT @@ -2098,14 +2091,13 @@ counter to RFC 2822, there's no separating newline here # test_defect_handling def test_lying_multipart(self): - unless = self.assertTrue msg = self._msgobj('msg_41.txt') - unless(hasattr(msg, 'defects')) + self.assertTrue(hasattr(msg, 'defects')) self.assertEqual(len(msg.defects), 2) - unless(isinstance(msg.defects[0], - errors.NoBoundaryInMultipartDefect)) - unless(isinstance(msg.defects[1], - errors.MultipartInvariantViolationDefect)) + self.assertIsInstance(msg.defects[0], + errors.NoBoundaryInMultipartDefect) + self.assertIsInstance(msg.defects[1], + errors.MultipartInvariantViolationDefect) # test_defect_handling def test_missing_start_boundary(self): @@ -2120,8 +2112,8 @@ counter to RFC 2822, there's no separating newline here # [*] This message is missing its start boundary bad = outer.get_payload(1).get_payload(0) self.assertEqual(len(bad.defects), 1) - self.assertTrue(isinstance(bad.defects[0], - errors.StartBoundaryNotFoundDefect)) + self.assertIsInstance(bad.defects[0], + errors.StartBoundaryNotFoundDefect) # test_defect_handling def test_first_line_is_continuation_header(self): @@ -2288,17 +2280,16 @@ class TestMIMEMessage(TestEmailBase): def test_valid_argument(self): eq = self.assertEqual - unless = self.assertTrue subject = 'A sub-message' m = Message() m['Subject'] = subject r = MIMEMessage(m) eq(r.get_content_type(), 'message/rfc822') payload = r.get_payload() - unless(isinstance(payload, list)) + self.assertIsInstance(payload, list) eq(len(payload), 1) subpart = payload[0] - unless(subpart is m) + self.assertIs(subpart, m) eq(subpart['subject'], subject) def test_bad_multipart(self): @@ -2331,24 +2322,22 @@ Here is the body of the message. def test_parse_message_rfc822(self): eq = self.assertEqual - unless = self.assertTrue msg = self._msgobj('msg_11.txt') eq(msg.get_content_type(), 'message/rfc822') payload = msg.get_payload() - unless(isinstance(payload, list)) + self.assertIsInstance(payload, list) eq(len(payload), 1) submsg = payload[0] - self.assertTrue(isinstance(submsg, Message)) + self.assertIsInstance(submsg, Message) eq(submsg['subject'], 'An enclosed message') eq(submsg.get_payload(), 'Here is the body of the message.\n') def test_dsn(self): eq = self.assertEqual - unless = self.assertTrue # msg 16 is a Delivery Status Notification, see RFC 1894 msg = self._msgobj('msg_16.txt') eq(msg.get_content_type(), 'multipart/report') - unless(msg.is_multipart()) + self.assertTrue(msg.is_multipart()) eq(len(msg.get_payload()), 3) # Subpart 1 is a text/plain, human readable section subpart = msg.get_payload(0) @@ -2377,13 +2366,13 @@ Your message cannot be delivered to the following recipients: # message/delivery-status should treat each block as a bunch of # headers, i.e. a bunch of Message objects. dsn1 = subpart.get_payload(0) - unless(isinstance(dsn1, Message)) + self.assertIsInstance(dsn1, Message) eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu') eq(dsn1.get_param('dns', header='reporting-mta'), '') # Try a missing one <wink> eq(dsn1.get_param('nsd', header='reporting-mta'), None) dsn2 = subpart.get_payload(1) - unless(isinstance(dsn2, Message)) + self.assertIsInstance(dsn2, Message) eq(dsn2['action'], 'failed') eq(dsn2.get_params(header='original-recipient'), [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')]) @@ -2392,10 +2381,10 @@ Your message cannot be delivered to the following recipients: subpart = msg.get_payload(2) eq(subpart.get_content_type(), 'message/rfc822') payload = subpart.get_payload() - unless(isinstance(payload, list)) + self.assertIsInstance(payload, list) eq(len(payload), 1) subsubpart = payload[0] - unless(isinstance(subsubpart, Message)) + self.assertIsInstance(subsubpart, Message) eq(subsubpart.get_content_type(), 'text/plain') eq(subsubpart['message-id'], '<002001c144a6$8752e060$56104586@oxy.edu>') @@ -2693,7 +2682,6 @@ class TestIdempotent(TestEmailBase): def test_content_type(self): eq = self.assertEqual - unless = self.assertTrue # Get a message object and reset the seek pointer for other tests msg, text = self._msgobj('msg_05.txt') eq(msg.get_content_type(), 'multipart/report') @@ -2715,29 +2703,28 @@ class TestIdempotent(TestEmailBase): eq(msg2.get_payload(), 'Yadda yadda yadda' + self.linesep) msg3 = msg.get_payload(2) eq(msg3.get_content_type(), 'message/rfc822') - self.assertTrue(isinstance(msg3, Message)) + self.assertIsInstance(msg3, Message) payload = msg3.get_payload() - unless(isinstance(payload, list)) + self.assertIsInstance(payload, list) eq(len(payload), 1) msg4 = payload[0] - unless(isinstance(msg4, Message)) + self.assertIsInstance(msg4, Message) eq(msg4.get_payload(), 'Yadda yadda yadda' + self.linesep) def test_parser(self): eq = self.assertEqual - unless = self.assertTrue msg, text = self._msgobj('msg_06.txt') # Check some of the outer headers eq(msg.get_content_type(), 'message/rfc822') # Make sure the payload is a list of exactly one sub-Message, and that # that submessage has a type of text/plain payload = msg.get_payload() - unless(isinstance(payload, list)) + self.assertIsInstance(payload, list) eq(len(payload), 1) msg1 = payload[0] - self.assertTrue(isinstance(msg1, Message)) + self.assertIsInstance(msg1, Message) eq(msg1.get_content_type(), 'text/plain') - self.assertTrue(isinstance(msg1.get_payload(), str)) + self.assertIsInstance(msg1.get_payload(), str) eq(msg1.get_payload(), self.linesep) @@ -2768,7 +2755,6 @@ class TestMiscellaneous(TestEmailBase): self.assertEqual(text, s.getvalue()) def test_message_from_string_with_class(self): - unless = self.assertTrue with openfile('msg_01.txt') as fp: text = fp.read() @@ -2777,35 +2763,34 @@ class TestMiscellaneous(TestEmailBase): pass msg = email.message_from_string(text, MyMessage) - unless(isinstance(msg, MyMessage)) + self.assertIsInstance(msg, MyMessage) # Try something more complicated with openfile('msg_02.txt') as fp: text = fp.read() msg = email.message_from_string(text, MyMessage) for subpart in msg.walk(): - unless(isinstance(subpart, MyMessage)) + self.assertIsInstance(subpart, MyMessage) def test_message_from_file_with_class(self): - unless = self.assertTrue # Create a subclass class MyMessage(Message): pass with openfile('msg_01.txt') as fp: msg = email.message_from_file(fp, MyMessage) - unless(isinstance(msg, MyMessage)) + self.assertIsInstance(msg, MyMessage) # Try something more complicated with openfile('msg_02.txt') as fp: msg = email.message_from_file(fp, MyMessage) for subpart in msg.walk(): - unless(isinstance(subpart, MyMessage)) + self.assertIsInstance(subpart, MyMessage) def test_custom_message_does_not_require_arguments(self): class MyMessage(Message): def __init__(self): super().__init__() msg = self._str_msg("Subject: test\n\ntest", MyMessage) - self.assertTrue(isinstance(msg, MyMessage)) + self.assertIsInstance(msg, MyMessage) def test__all__(self): module = __import__('email') @@ -3295,9 +3280,9 @@ Do you like this message? break om.append(ol) n1 += 1 - self.assertTrue(n == n1) - self.assertTrue(len(om) == nt) - self.assertTrue(''.join([il for il, n in imt]) == ''.join(om)) + self.assertEqual(n, n1) + self.assertEqual(len(om), nt) + self.assertEqual(''.join([il for il, n in imt]), ''.join(om)) @@ -3312,7 +3297,7 @@ class TestParsers(TestEmailBase): eq(msg['to'], 'ppp@zzz.org') eq(msg.get_content_type(), 'multipart/mixed') self.assertFalse(msg.is_multipart()) - self.assertTrue(isinstance(msg.get_payload(), str)) + self.assertIsInstance(msg.get_payload(), str) def test_bytes_header_parser(self): eq = self.assertEqual @@ -3323,8 +3308,8 @@ class TestParsers(TestEmailBase): eq(msg['to'], 'ppp@zzz.org') eq(msg.get_content_type(), 'multipart/mixed') self.assertFalse(msg.is_multipart()) - self.assertTrue(isinstance(msg.get_payload(), str)) - self.assertTrue(isinstance(msg.get_payload(decode=True), bytes)) + self.assertIsInstance(msg.get_payload(), str) + self.assertIsInstance(msg.get_payload(decode=True), bytes) def test_whitespace_continuation(self): eq = self.assertEqual @@ -4365,7 +4350,7 @@ class TestHeader(TestEmailBase): h = Header("I am the very model of a modern Major-General; I've information vegetable, animal, and mineral; I know the kings of England, and I quote the fights historical from Marathon to Waterloo, in order categorical; I'm very well acquainted, too, with matters mathematical; I understand equations, both the simple and quadratical; about binomial theorem I'm teeming with a lot o' news, with many cheerful facts about the square of the hypotenuse.", maxlinelen=76) for l in h.encode(splitchars=' ').split('\n '): - self.assertTrue(len(l) <= 76) + self.assertLessEqual(len(l), 76) def test_multilingual(self): eq = self.ndiffAssertEqual @@ -4834,7 +4819,7 @@ Content-Type: text/html; NAME*0=file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOC ''' msg = email.message_from_string(m) param = msg.get_param('NAME') - self.assertFalse(isinstance(param, tuple)) + self.assertNotIsInstance(param, tuple) self.assertEqual( param, 'file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm') @@ -4993,7 +4978,7 @@ Content-Type: application/x-foo; name*0=\"Frank's\"; name*1=\" Document\" """ msg = email.message_from_string(m) param = msg.get_param('name') - self.assertFalse(isinstance(param, tuple)) + self.assertNotIsInstance(param, tuple) self.assertEqual(param, "Frank's Document") # test_headerregistry.TestContentTypeHeader.rfc2231_single_quote_in_value_with_charset_and_lang @@ -5019,7 +5004,7 @@ Content-Type: application/x-foo; """ msg = email.message_from_string(m) param = msg.get_param('name') - self.assertFalse(isinstance(param, tuple)) + self.assertNotIsInstance(param, tuple) self.assertEqual(param, "us-ascii'en-us'Frank's Document") # test_headerregistry.TestContentTypeHeader.rfc2231_single_quotes_inside_quotes diff --git a/Lib/test/test_email/test_parser.py b/Lib/test/test_email/test_parser.py index 3abd11a45c..b54fdd7589 100644 --- a/Lib/test/test_email/test_parser.py +++ b/Lib/test/test_email/test_parser.py @@ -18,7 +18,7 @@ class TestCustomMessage(TestEmailBase): msg = email.message_from_string("Subject: bogus\n\nmsg\n", self.MyMessage, policy=self.MyPolicy) - self.assertTrue(isinstance(msg, self.MyMessage)) + self.assertIsInstance(msg, self.MyMessage) self.assertIs(msg.check_policy, self.MyPolicy) def test_custom_message_gets_policy_if_possible_from_file(self): @@ -26,7 +26,7 @@ class TestCustomMessage(TestEmailBase): msg = email.message_from_file(source_file, self.MyMessage, policy=self.MyPolicy) - self.assertTrue(isinstance(msg, self.MyMessage)) + self.assertIsInstance(msg, self.MyMessage) self.assertIs(msg.check_policy, self.MyPolicy) # XXX add tests for other functions that take Message arg. diff --git a/Lib/test/test_email/test_utils.py b/Lib/test/test_email/test_utils.py index e507dd2c03..4abdc04f34 100644 --- a/Lib/test/test_email/test_utils.py +++ b/Lib/test/test_email/test_utils.py @@ -54,12 +54,12 @@ class LocaltimeTests(unittest.TestCase): def test_localtime_is_tz_aware_daylight_true(self): test.support.patch(self, time, 'daylight', True) t = utils.localtime() - self.assertIsNot(t.tzinfo, None) + self.assertIsNotNone(t.tzinfo) def test_localtime_is_tz_aware_daylight_false(self): test.support.patch(self, time, 'daylight', False) t = utils.localtime() - self.assertIsNot(t.tzinfo, None) + self.assertIsNotNone(t.tzinfo) def test_localtime_daylight_true_dst_false(self): test.support.patch(self, time, 'daylight', True) diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py index 2e904cf878..4af217b733 100644 --- a/Lib/test/test_enumerate.py +++ b/Lib/test/test_enumerate.py @@ -204,11 +204,10 @@ class TestReversed(unittest.TestCase, PickleTest): self.assertRaises(TypeError, reversed) self.assertRaises(TypeError, reversed, [], 'extra') + @unittest.skipUnless(hasattr(sys, 'getrefcount'), 'test needs sys.getrefcount()') def test_bug1229429(self): # this bug was never in reversed, it was in # PyObject_CallMethod, and reversed_new calls that sometimes. - if not hasattr(sys, "getrefcount"): - return def f(): pass r = f.__reversed__ = object() diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py index 4686183618..64cd80bc14 100644 --- a/Lib/test/test_ftplib.py +++ b/Lib/test/test_ftplib.py @@ -16,7 +16,7 @@ try: except ImportError: ssl = None -from unittest import TestCase +from unittest import TestCase, skipUnless from test import support from test.support import HOST, HOSTv6 threading = support.import_module('threading') @@ -779,6 +779,7 @@ class TestFTPClass(TestCase): self.assertRaises(ftplib.Error, self.client.storlines, 'stor', f) +@skipUnless(support.IPV6_ENABLED, "IPv6 not enabled") class TestIPv6Environment(TestCase): def setUp(self): @@ -819,6 +820,7 @@ class TestIPv6Environment(TestCase): retr() +@skipUnless(ssl, "SSL not available") class TestTLS_FTPClassMixin(TestFTPClass): """Repeat TestFTPClass tests starting the TLS layer for both control and data connections first. @@ -834,6 +836,7 @@ class TestTLS_FTPClassMixin(TestFTPClass): self.client.prot_p() +@skipUnless(ssl, "SSL not available") class TestTLS_FTPClass(TestCase): """Specific TLS_FTP class tests.""" @@ -1015,12 +1018,9 @@ class TestTimeouts(TestCase): def test_main(): - tests = [TestFTPClass, TestTimeouts] - if support.IPV6_ENABLED: - tests.append(TestIPv6Environment) - - if ssl is not None: - tests.extend([TestTLS_FTPClassMixin, TestTLS_FTPClass]) + tests = [TestFTPClass, TestTimeouts, + TestIPv6Environment, + TestTLS_FTPClassMixin, TestTLS_FTPClass] thread_info = support.threading_setup() try: diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index b15b6fd4c6..c977a9dd4d 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -229,6 +229,11 @@ text self._parse_error("<a foo='bar") self._parse_error("<a foo='>'") self._parse_error("<a foo='>") + self._parse_error("<a$>") + self._parse_error("<a$b>") + self._parse_error("<a$b/>") + self._parse_error("<a$b >") + self._parse_error("<a$b />") def test_valid_doctypes(self): # from http://www.w3.org/QA/2002/04/valid-dtd-list.html @@ -368,8 +373,8 @@ class HTMLParserTolerantTestCase(HTMLParserStrictTestCase): ('starttag', 'html', [('<html', None)]), ('data', 'te>>xt'), ('entityref', 'a'), - ('data', '<<bc'), - ('endtag', 'a'), + ('data', '<'), + ('starttag', 'bc<', [('a', None)]), ('endtag', 'html'), ('data', '\n<img src="URL>'), ('comment', '/img'), @@ -380,8 +385,7 @@ class HTMLParserTolerantTestCase(HTMLParserStrictTestCase): self._run_check("</$>", [('comment', '$')]) self._run_check("</", [('data', '</')]) self._run_check("</a", [('data', '</a')]) - # XXX this might be wrong - self._run_check("<a<a>", [('data', '<a'), ('starttag', 'a', [])]) + self._run_check("<a<a>", [('starttag', 'a<a', [])]) self._run_check("</a<a>", [('endtag', 'a<a')]) self._run_check("<!", [('data', '<!')]) self._run_check("<a", [('data', '<a')]) @@ -389,6 +393,11 @@ class HTMLParserTolerantTestCase(HTMLParserStrictTestCase): self._run_check("<a foo='bar", [('data', "<a foo='bar")]) self._run_check("<a foo='>'", [('data', "<a foo='>'")]) self._run_check("<a foo='>", [('data', "<a foo='>")]) + self._run_check("<a$>", [('starttag', 'a$', [])]) + self._run_check("<a$b>", [('starttag', 'a$b', [])]) + self._run_check("<a$b/>", [('startendtag', 'a$b', [])]) + self._run_check("<a$b >", [('starttag', 'a$b', [])]) + self._run_check("<a$b />", [('startendtag', 'a$b', [])]) def test_slashes_in_starttag(self): self._run_check('<a foo="var"/>', [('startendtag', 'a', [('foo', 'var')])]) @@ -753,11 +762,5 @@ class AttributesTolerantTestCase(AttributesStrictTestCase): ("data", "spam"), ("endtag", "a")]) - -def test_main(): - support.run_unittest(HTMLParserStrictTestCase, HTMLParserTolerantTestCase, - AttributesStrictTestCase, AttributesTolerantTestCase) - - if __name__ == "__main__": - test_main() + unittest.main() diff --git a/Lib/test/test_lzma.py b/Lib/test/test_lzma.py index a13cf3bd09..ad9045604e 100644 --- a/Lib/test/test_lzma.py +++ b/Lib/test/test_lzma.py @@ -1,5 +1,6 @@ from io import BytesIO, UnsupportedOperation import os +import pickle import random import unittest @@ -216,6 +217,14 @@ class CompressorDecompressorTestCase(unittest.TestCase): finally: input = cdata = ddata = None + # Pickling raises an exception; there's no way to serialize an lzma_stream. + + def test_pickle(self): + with self.assertRaises(TypeError): + pickle.dumps(LZMACompressor()) + with self.assertRaises(TypeError): + pickle.dumps(LZMADecompressor()) + class CompressDecompressFunctionTestCase(unittest.TestCase): diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 39e8643414..f2e4c63240 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -868,10 +868,10 @@ class TestMaildir(TestMailbox, unittest.TestCase): for msg in self._box: pass + @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()') + @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()') def test_file_permissions(self): # Verify that message files are created without execute permissions - if not hasattr(os, "stat") or not hasattr(os, "umask"): - return msg = mailbox.MaildirMessage(self._template % 0) orig_umask = os.umask(0) try: @@ -882,12 +882,11 @@ class TestMaildir(TestMailbox, unittest.TestCase): mode = os.stat(path).st_mode self.assertFalse(mode & 0o111) + @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()') + @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()') def test_folder_file_perms(self): # From bug #3228, we want to verify that the file created inside a Maildir # subfolder isn't marked as executable. - if not hasattr(os, "stat") or not hasattr(os, "umask"): - return - orig_umask = os.umask(0) try: subfolder = self._box.add_folder('subfolder') @@ -1097,24 +1096,25 @@ class TestMbox(_TestMboxMMDF, unittest.TestCase): _factory = lambda self, path, factory=None: mailbox.mbox(path, factory) + @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()') + @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()') def test_file_perms(self): # From bug #3228, we want to verify that the mailbox file isn't executable, # even if the umask is set to something that would leave executable bits set. # We only run this test on platforms that support umask. - if hasattr(os, 'umask') and hasattr(os, 'stat'): - try: - old_umask = os.umask(0o077) - self._box.close() - os.unlink(self._path) - self._box = mailbox.mbox(self._path, create=True) - self._box.add('') - self._box.close() - finally: - os.umask(old_umask) + try: + old_umask = os.umask(0o077) + self._box.close() + os.unlink(self._path) + self._box = mailbox.mbox(self._path, create=True) + self._box.add('') + self._box.close() + finally: + os.umask(old_umask) - st = os.stat(self._path) - perms = st.st_mode - self.assertFalse((perms & 0o111)) # Execute bits should all be off. + st = os.stat(self._path) + perms = st.st_mode + self.assertFalse((perms & 0o111)) # Execute bits should all be off. def test_terminating_newline(self): message = email.message.Message() diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 715003af05..48f84ba732 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -980,38 +980,37 @@ class MathTests(unittest.TestCase): # still fails this part of the test on some platforms. For now, we only # *run* test_exceptions() in verbose mode, so that this isn't normally # tested. + @unittest.skipUnless(verbose, 'requires verbose mode') + def test_exceptions(self): + try: + x = math.exp(-1000000000) + except: + # mathmodule.c is failing to weed out underflows from libm, or + # we've got an fp format with huge dynamic range + self.fail("underflowing exp() should not have raised " + "an exception") + if x != 0: + self.fail("underflowing exp() should have returned 0") + + # If this fails, probably using a strict IEEE-754 conforming libm, and x + # is +Inf afterwards. But Python wants overflows detected by default. + try: + x = math.exp(1000000000) + except OverflowError: + pass + else: + self.fail("overflowing exp() didn't trigger OverflowError") - if verbose: - def test_exceptions(self): - try: - x = math.exp(-1000000000) - except: - # mathmodule.c is failing to weed out underflows from libm, or - # we've got an fp format with huge dynamic range - self.fail("underflowing exp() should not have raised " - "an exception") - if x != 0: - self.fail("underflowing exp() should have returned 0") - - # If this fails, probably using a strict IEEE-754 conforming libm, and x - # is +Inf afterwards. But Python wants overflows detected by default. - try: - x = math.exp(1000000000) - except OverflowError: - pass - else: - self.fail("overflowing exp() didn't trigger OverflowError") - - # If this fails, it could be a puzzle. One odd possibility is that - # mathmodule.c's macros are getting confused while comparing - # Inf (HUGE_VAL) to a NaN, and artificially setting errno to ERANGE - # as a result (and so raising OverflowError instead). - try: - x = math.sqrt(-1.0) - except ValueError: - pass - else: - self.fail("sqrt(-1) didn't raise ValueError") + # If this fails, it could be a puzzle. One odd possibility is that + # mathmodule.c's macros are getting confused while comparing + # Inf (HUGE_VAL) to a NaN, and artificially setting errno to ERANGE + # as a result (and so raising OverflowError instead). + try: + x = math.sqrt(-1.0) + except ValueError: + pass + else: + self.fail("sqrt(-1) didn't raise ValueError") @requires_IEEE_754 def test_testfile(self): diff --git a/Lib/test/test_mmap.py b/Lib/test/test_mmap.py index d066368726..899df8d818 100644 --- a/Lib/test/test_mmap.py +++ b/Lib/test/test_mmap.py @@ -314,26 +314,25 @@ class MmapTests(unittest.TestCase): mf.close() f.close() + @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()") def test_entire_file(self): # test mapping of entire file by passing 0 for map length - if hasattr(os, "stat"): - f = open(TESTFN, "wb+") + f = open(TESTFN, "wb+") - f.write(2**16 * b'm') # Arbitrary character - f.close() + f.write(2**16 * b'm') # Arbitrary character + f.close() - f = open(TESTFN, "rb+") - mf = mmap.mmap(f.fileno(), 0) - self.assertEqual(len(mf), 2**16, "Map size should equal file size.") - self.assertEqual(mf.read(2**16), 2**16 * b"m") - mf.close() - f.close() + f = open(TESTFN, "rb+") + mf = mmap.mmap(f.fileno(), 0) + self.assertEqual(len(mf), 2**16, "Map size should equal file size.") + self.assertEqual(mf.read(2**16), 2**16 * b"m") + mf.close() + f.close() + @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()") def test_length_0_offset(self): # Issue #10916: test mapping of remainder of file by passing 0 for # map length with an offset doesn't cause a segfault. - if not hasattr(os, "stat"): - self.skipTest("needs os.stat") # NOTE: allocation granularity is currently 65536 under Win64, # and therefore the minimum offset alignment. with open(TESTFN, "wb") as f: @@ -343,12 +342,10 @@ class MmapTests(unittest.TestCase): with mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ) as mf: self.assertRaises(IndexError, mf.__getitem__, 80000) + @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()") def test_length_0_large_offset(self): # Issue #10959: test mapping of a file by passing 0 for # map length with a large offset doesn't cause a segfault. - if not hasattr(os, "stat"): - self.skipTest("needs os.stat") - with open(TESTFN, "wb") as f: f.write(115699 * b'm') # Arbitrary character @@ -560,9 +557,8 @@ class MmapTests(unittest.TestCase): return mmap.mmap.__new__(klass, -1, *args, **kwargs) anon_mmap(PAGESIZE) + @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ") def test_prot_readonly(self): - if not hasattr(mmap, 'PROT_READ'): - return mapsize = 10 with open(TESTFN, "wb") as fp: fp.write(b"a"*mapsize) @@ -616,67 +612,69 @@ class MmapTests(unittest.TestCase): self.assertEqual(m.read_byte(), b) m.close() - if os.name == 'nt': - def test_tagname(self): - data1 = b"0123456789" - data2 = b"abcdefghij" - assert len(data1) == len(data2) - - # Test same tag - m1 = mmap.mmap(-1, len(data1), tagname="foo") - m1[:] = data1 - m2 = mmap.mmap(-1, len(data2), tagname="foo") - m2[:] = data2 - self.assertEqual(m1[:], data2) - self.assertEqual(m2[:], data2) - m2.close() - m1.close() - - # Test different tag - m1 = mmap.mmap(-1, len(data1), tagname="foo") - m1[:] = data1 - m2 = mmap.mmap(-1, len(data2), tagname="boo") - m2[:] = data2 - self.assertEqual(m1[:], data1) - self.assertEqual(m2[:], data2) - m2.close() - m1.close() - - def test_crasher_on_windows(self): - # Should not crash (Issue 1733986) - m = mmap.mmap(-1, 1000, tagname="foo") - try: - mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size - except: - pass - m.close() + @unittest.skipUnless(os.name == 'nt', 'requires Windows') + def test_tagname(self): + data1 = b"0123456789" + data2 = b"abcdefghij" + assert len(data1) == len(data2) + + # Test same tag + m1 = mmap.mmap(-1, len(data1), tagname="foo") + m1[:] = data1 + m2 = mmap.mmap(-1, len(data2), tagname="foo") + m2[:] = data2 + self.assertEqual(m1[:], data2) + self.assertEqual(m2[:], data2) + m2.close() + m1.close() + + # Test different tag + m1 = mmap.mmap(-1, len(data1), tagname="foo") + m1[:] = data1 + m2 = mmap.mmap(-1, len(data2), tagname="boo") + m2[:] = data2 + self.assertEqual(m1[:], data1) + self.assertEqual(m2[:], data2) + m2.close() + m1.close() + + @unittest.skipUnless(os.name == 'nt', 'requires Windows') + def test_crasher_on_windows(self): + # Should not crash (Issue 1733986) + m = mmap.mmap(-1, 1000, tagname="foo") + try: + mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size + except: + pass + m.close() - # Should not crash (Issue 5385) - with open(TESTFN, "wb") as fp: - fp.write(b"x"*10) - f = open(TESTFN, "r+b") - m = mmap.mmap(f.fileno(), 0) - f.close() - try: - m.resize(0) # will raise WindowsError - except: - pass - try: - m[:] - except: - pass - m.close() + # Should not crash (Issue 5385) + with open(TESTFN, "wb") as fp: + fp.write(b"x"*10) + f = open(TESTFN, "r+b") + m = mmap.mmap(f.fileno(), 0) + f.close() + try: + m.resize(0) # will raise WindowsError + except: + pass + try: + m[:] + except: + pass + m.close() - def test_invalid_descriptor(self): - # socket file descriptors are valid, but out of range - # for _get_osfhandle, causing a crash when validating the - # parameters to _get_osfhandle. - s = socket.socket() - try: - with self.assertRaises(mmap.error): - m = mmap.mmap(s.fileno(), 10) - finally: - s.close() + @unittest.skipUnless(os.name == 'nt', 'requires Windows') + def test_invalid_descriptor(self): + # socket file descriptors are valid, but out of range + # for _get_osfhandle, causing a crash when validating the + # parameters to _get_osfhandle. + s = socket.socket() + try: + with self.assertRaises(mmap.error): + m = mmap.mmap(s.fileno(), 10) + finally: + s.close() def test_context_manager(self): with mmap.mmap(-1, 10) as m: diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py index d5582aab82..86cf5c188f 100644 --- a/Lib/test/test_multiprocessing.py +++ b/Lib/test/test_multiprocessing.py @@ -716,7 +716,7 @@ class _TestQueue(BaseTestCase): start = time.time() self.assertRaises(pyqueue.Empty, q.get, True, 0.2) delta = time.time() - start - self.assertGreaterEqual(delta, 0.19) + self.assertGreaterEqual(delta, 0.18) # # @@ -1691,6 +1691,16 @@ class _TestPool(BaseTestCase): self.assertEqual(2, len(call_args)) self.assertIsInstance(call_args[1], ValueError) + def test_map_unplicklable(self): + # Issue #19425 -- failure to pickle should not cause a hang + if self.TYPE == 'threads': + return + class A(object): + def __reduce__(self): + raise RuntimeError('cannot pickle') + with self.assertRaises(RuntimeError): + self.pool.map(sqr, [A()]*10) + def test_map_chunksize(self): try: self.pool.map_async(sqr, [], chunksize=1).get(timeout=TIMEOUT1) diff --git a/Lib/test/test_nntplib.py b/Lib/test/test_nntplib.py index 5ab87c48bb..1d52713f07 100644 --- a/Lib/test/test_nntplib.py +++ b/Lib/test/test_nntplib.py @@ -6,10 +6,12 @@ import unittest import functools import contextlib from test import support -from nntplib import NNTP, GroupInfo, _have_ssl +from nntplib import NNTP, GroupInfo import nntplib -if _have_ssl: +try: import ssl +except ImportError: + ssl = None TIMEOUT = 30 @@ -199,23 +201,23 @@ class NetworkedNNTPTestsMixin: resp, caps = self.server.capabilities() _check_caps(caps) - if _have_ssl: - def test_starttls(self): - file = self.server.file - sock = self.server.sock - try: - self.server.starttls() - except nntplib.NNTPPermanentError: - self.skipTest("STARTTLS not supported by server.") - else: - # Check that the socket and internal pseudo-file really were - # changed. - self.assertNotEqual(file, self.server.file) - self.assertNotEqual(sock, self.server.sock) - # Check that the new socket really is an SSL one - self.assertIsInstance(self.server.sock, ssl.SSLSocket) - # Check that trying starttls when it's already active fails. - self.assertRaises(ValueError, self.server.starttls) + @unittest.skipUnless(ssl, 'requires SSL support') + def test_starttls(self): + file = self.server.file + sock = self.server.sock + try: + self.server.starttls() + except nntplib.NNTPPermanentError: + self.skipTest("STARTTLS not supported by server.") + else: + # Check that the socket and internal pseudo-file really were + # changed. + self.assertNotEqual(file, self.server.file) + self.assertNotEqual(sock, self.server.sock) + # Check that the new socket really is an SSL one + self.assertIsInstance(self.server.sock, ssl.SSLSocket) + # Check that trying starttls when it's already active fails. + self.assertRaises(ValueError, self.server.starttls) def test_zlogin(self): # This test must be the penultimate because further commands will be @@ -300,25 +302,24 @@ class NetworkedNNTPTests(NetworkedNNTPTestsMixin, unittest.TestCase): if cls.server is not None: cls.server.quit() +@unittest.skipUnless(ssl, 'requires SSL support') +class NetworkedNNTP_SSLTests(NetworkedNNTPTests): -if _have_ssl: - class NetworkedNNTP_SSLTests(NetworkedNNTPTests): - - # Technical limits for this public NNTP server (see http://www.aioe.org): - # "Only two concurrent connections per IP address are allowed and - # 400 connections per day are accepted from each IP address." + # Technical limits for this public NNTP server (see http://www.aioe.org): + # "Only two concurrent connections per IP address are allowed and + # 400 connections per day are accepted from each IP address." - NNTP_HOST = 'nntp.aioe.org' - GROUP_NAME = 'comp.lang.python' - GROUP_PAT = 'comp.lang.*' + NNTP_HOST = 'nntp.aioe.org' + GROUP_NAME = 'comp.lang.python' + GROUP_PAT = 'comp.lang.*' - NNTP_CLASS = nntplib.NNTP_SSL + NNTP_CLASS = getattr(nntplib, 'NNTP_SSL', None) - # Disabled as it produces too much data - test_list = None + # Disabled as it produces too much data + test_list = None - # Disabled as the connection will already be encrypted. - test_starttls = None + # Disabled as the connection will already be encrypted. + test_starttls = None # @@ -1407,12 +1408,13 @@ class MiscTests(unittest.TestCase): gives(2000, 6, 23, "000623", "000000") gives(2010, 6, 5, "100605", "000000") + @unittest.skipUnless(ssl, 'requires SSL support') + def test_ssl_support(self): + self.assertTrue(hasattr(nntplib, 'NNTP_SSL')) def test_main(): tests = [MiscTests, NNTPv1Tests, NNTPv2Tests, CapsAfterLoginNNTPv2Tests, - SendReaderNNTPv2Tests, NetworkedNNTPTests] - if _have_ssl: - tests.append(NetworkedNNTP_SSLTests) + SendReaderNNTPv2Tests, NetworkedNNTPTests, NetworkedNNTP_SSLTests] support.run_unittest(*tests) diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index d2424d7865..601c6b2e97 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -178,10 +178,8 @@ class StatAttributeTests(unittest.TestCase): os.unlink(self.fname) os.rmdir(support.TESTFN) + @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()') def check_stat_attributes(self, fname): - if not hasattr(os, "stat"): - return - result = os.stat(fname) # Make sure direct access works @@ -258,10 +256,8 @@ class StatAttributeTests(unittest.TestCase): warnings.simplefilter("ignore", DeprecationWarning) self.check_stat_attributes(fname) + @unittest.skipUnless(hasattr(os, 'statvfs'), 'test needs os.statvfs()') def test_statvfs_attributes(self): - if not hasattr(os, "statvfs"): - return - try: result = os.statvfs(self.fname) except OSError as e: @@ -450,10 +446,10 @@ class StatAttributeTests(unittest.TestCase): os.close(dirfd) self._test_utime_subsecond(set_time) - # Restrict test to Win32, since there is no guarantee other + # Restrict tests to Win32, since there is no guarantee other # systems support centiseconds - if sys.platform == 'win32': - def get_file_system(path): + def get_file_system(path): + if sys.platform == 'win32': root = os.path.splitdrive(os.path.abspath(path))[0] + '\\' import ctypes kernel32 = ctypes.windll.kernel32 @@ -461,38 +457,45 @@ class StatAttributeTests(unittest.TestCase): if kernel32.GetVolumeInformationW(root, None, 0, None, None, None, buf, len(buf)): return buf.value - if get_file_system(support.TESTFN) == "NTFS": - def test_1565150(self): - t1 = 1159195039.25 - os.utime(self.fname, (t1, t1)) - self.assertEqual(os.stat(self.fname).st_mtime, t1) - - def test_large_time(self): - t1 = 5000000000 # some day in 2128 - os.utime(self.fname, (t1, t1)) - self.assertEqual(os.stat(self.fname).st_mtime, t1) + @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") + @unittest.skipUnless(get_file_system(support.TESTFN) == "NTFS", + "requires NTFS") + def test_1565150(self): + t1 = 1159195039.25 + os.utime(self.fname, (t1, t1)) + self.assertEqual(os.stat(self.fname).st_mtime, t1) + + @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") + @unittest.skipUnless(get_file_system(support.TESTFN) == "NTFS", + "requires NTFS") + def test_large_time(self): + t1 = 5000000000 # some day in 2128 + os.utime(self.fname, (t1, t1)) + self.assertEqual(os.stat(self.fname).st_mtime, t1) + + @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") + def test_1686475(self): + # Verify that an open file can be stat'ed + try: + os.stat(r"c:\pagefile.sys") + except WindowsError as e: + if e.errno == 2: # file does not exist; cannot run test + return + self.fail("Could not stat pagefile.sys") - def test_1686475(self): - # Verify that an open file can be stat'ed - try: - os.stat(r"c:\pagefile.sys") - except WindowsError as e: - if e.errno == 2: # file does not exist; cannot run test - return - self.fail("Could not stat pagefile.sys") - - @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") - def test_15261(self): - # Verify that stat'ing a closed fd does not cause crash - r, w = os.pipe() - try: - os.stat(r) # should not raise error - finally: - os.close(r) - os.close(w) - with self.assertRaises(OSError) as ctx: - os.stat(r) - self.assertEqual(ctx.exception.errno, errno.EBADF) + @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") + @unittest.skipUnless(hasattr(os, "pipe"), "requires os.pipe()") + def test_15261(self): + # Verify that stat'ing a closed fd does not cause crash + r, w = os.pipe() + try: + os.stat(r) # should not raise error + finally: + os.close(r) + os.close(w) + with self.assertRaises(OSError) as ctx: + os.stat(r) + self.assertEqual(ctx.exception.errno, errno.EBADF) from test import mapping_tests @@ -1127,6 +1130,7 @@ class ExecTests(unittest.TestCase): self._test_internal_execvpe(bytes) +@unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") class Win32ErrorTests(unittest.TestCase): def test_rename(self): self.assertRaises(WindowsError, os.rename, support.TESTFN, support.TESTFN+".bak") @@ -1173,63 +1177,63 @@ class TestInvalidFD(unittest.TestCase): self.fail("%r didn't raise a OSError with a bad file descriptor" % f) + @unittest.skipUnless(hasattr(os, 'isatty'), 'test needs os.isatty()') def test_isatty(self): - if hasattr(os, "isatty"): - self.assertEqual(os.isatty(support.make_bad_fd()), False) + self.assertEqual(os.isatty(support.make_bad_fd()), False) + @unittest.skipUnless(hasattr(os, 'closerange'), 'test needs os.closerange()') def test_closerange(self): - if hasattr(os, "closerange"): - fd = support.make_bad_fd() - # Make sure none of the descriptors we are about to close are - # currently valid (issue 6542). - for i in range(10): - try: os.fstat(fd+i) - except OSError: - pass - else: - break - if i < 2: - raise unittest.SkipTest( - "Unable to acquire a range of invalid file descriptors") - self.assertEqual(os.closerange(fd, fd + i-1), None) + fd = support.make_bad_fd() + # Make sure none of the descriptors we are about to close are + # currently valid (issue 6542). + for i in range(10): + try: os.fstat(fd+i) + except OSError: + pass + else: + break + if i < 2: + raise unittest.SkipTest( + "Unable to acquire a range of invalid file descriptors") + self.assertEqual(os.closerange(fd, fd + i-1), None) + @unittest.skipUnless(hasattr(os, 'dup2'), 'test needs os.dup2()') def test_dup2(self): - if hasattr(os, "dup2"): - self.check(os.dup2, 20) + self.check(os.dup2, 20) + @unittest.skipUnless(hasattr(os, 'fchmod'), 'test needs os.fchmod()') def test_fchmod(self): - if hasattr(os, "fchmod"): - self.check(os.fchmod, 0) + self.check(os.fchmod, 0) + @unittest.skipUnless(hasattr(os, 'fchown'), 'test needs os.fchown()') def test_fchown(self): - if hasattr(os, "fchown"): - self.check(os.fchown, -1, -1) + self.check(os.fchown, -1, -1) + @unittest.skipUnless(hasattr(os, 'fpathconf'), 'test needs os.fpathconf()') def test_fpathconf(self): - if hasattr(os, "fpathconf"): - self.check(os.pathconf, "PC_NAME_MAX") - self.check(os.fpathconf, "PC_NAME_MAX") + self.check(os.pathconf, "PC_NAME_MAX") + self.check(os.fpathconf, "PC_NAME_MAX") + @unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()') def test_ftruncate(self): - if hasattr(os, "ftruncate"): - self.check(os.truncate, 0) - self.check(os.ftruncate, 0) + self.check(os.truncate, 0) + self.check(os.ftruncate, 0) + @unittest.skipUnless(hasattr(os, 'lseek'), 'test needs os.lseek()') def test_lseek(self): - if hasattr(os, "lseek"): - self.check(os.lseek, 0, 0) + self.check(os.lseek, 0, 0) + @unittest.skipUnless(hasattr(os, 'read'), 'test needs os.read()') def test_read(self): - if hasattr(os, "read"): - self.check(os.read, 1) + self.check(os.read, 1) + @unittest.skipUnless(hasattr(os, 'tcsetpgrp'), 'test needs os.tcsetpgrp()') def test_tcsetpgrpt(self): - if hasattr(os, "tcsetpgrp"): - self.check(os.tcsetpgrp, 0) + self.check(os.tcsetpgrp, 0) + @unittest.skipUnless(hasattr(os, 'write'), 'test needs os.write()') def test_write(self): - if hasattr(os, "write"): - self.check(os.write, b" ") + self.check(os.write, b" ") class LinkTests(unittest.TestCase): @@ -1269,138 +1273,117 @@ class LinkTests(unittest.TestCase): self.file2 = self.file1 + "2" self._test_link(self.file1, self.file2) -if sys.platform != 'win32': - class Win32ErrorTests(unittest.TestCase): - pass - - class PosixUidGidTests(unittest.TestCase): - if hasattr(os, 'setuid'): - def test_setuid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.setuid, 0) - self.assertRaises(OverflowError, os.setuid, 1<<32) - - if hasattr(os, 'setgid'): - def test_setgid(self): - if os.getuid() != 0 and not HAVE_WHEEL_GROUP: - self.assertRaises(os.error, os.setgid, 0) - self.assertRaises(OverflowError, os.setgid, 1<<32) - - if hasattr(os, 'seteuid'): - def test_seteuid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.seteuid, 0) - self.assertRaises(OverflowError, os.seteuid, 1<<32) - - if hasattr(os, 'setegid'): - def test_setegid(self): - if os.getuid() != 0 and not HAVE_WHEEL_GROUP: - self.assertRaises(os.error, os.setegid, 0) - self.assertRaises(OverflowError, os.setegid, 1<<32) - - if hasattr(os, 'setreuid'): - def test_setreuid(self): - if os.getuid() != 0: - self.assertRaises(os.error, os.setreuid, 0, 0) - self.assertRaises(OverflowError, os.setreuid, 1<<32, 0) - self.assertRaises(OverflowError, os.setreuid, 0, 1<<32) - - def test_setreuid_neg1(self): - # Needs to accept -1. We run this in a subprocess to avoid - # altering the test runner's process state (issue8045). - subprocess.check_call([ - sys.executable, '-c', - 'import os,sys;os.setreuid(-1,-1);sys.exit(0)']) - - if hasattr(os, 'setregid'): - def test_setregid(self): - if os.getuid() != 0 and not HAVE_WHEEL_GROUP: - self.assertRaises(os.error, os.setregid, 0, 0) - self.assertRaises(OverflowError, os.setregid, 1<<32, 0) - self.assertRaises(OverflowError, os.setregid, 0, 1<<32) - - def test_setregid_neg1(self): - # Needs to accept -1. We run this in a subprocess to avoid - # altering the test runner's process state (issue8045). - subprocess.check_call([ - sys.executable, '-c', - 'import os,sys;os.setregid(-1,-1);sys.exit(0)']) - - class Pep383Tests(unittest.TestCase): - def setUp(self): - if support.TESTFN_UNENCODABLE: - self.dir = support.TESTFN_UNENCODABLE - elif support.TESTFN_NONASCII: - self.dir = support.TESTFN_NONASCII - else: - self.dir = support.TESTFN - self.bdir = os.fsencode(self.dir) - - bytesfn = [] - def add_filename(fn): - try: - fn = os.fsencode(fn) - except UnicodeEncodeError: - return - bytesfn.append(fn) - add_filename(support.TESTFN_UNICODE) - if support.TESTFN_UNENCODABLE: - add_filename(support.TESTFN_UNENCODABLE) - if support.TESTFN_NONASCII: - add_filename(support.TESTFN_NONASCII) - if not bytesfn: - self.skipTest("couldn't create any non-ascii filename") - - self.unicodefn = set() - os.mkdir(self.dir) - try: - for fn in bytesfn: - support.create_empty_file(os.path.join(self.bdir, fn)) - fn = os.fsdecode(fn) - if fn in self.unicodefn: - raise ValueError("duplicate filename") - self.unicodefn.add(fn) - except: - shutil.rmtree(self.dir) - raise +@unittest.skipIf(sys.platform == "win32", "Posix specific tests") +class PosixUidGidTests(unittest.TestCase): + @unittest.skipUnless(hasattr(os, 'setuid'), 'test needs os.setuid()') + def test_setuid(self): + if os.getuid() != 0: + self.assertRaises(os.error, os.setuid, 0) + self.assertRaises(OverflowError, os.setuid, 1<<32) + + @unittest.skipUnless(hasattr(os, 'setgid'), 'test needs os.setgid()') + def test_setgid(self): + if os.getuid() != 0 and not HAVE_WHEEL_GROUP: + self.assertRaises(os.error, os.setgid, 0) + self.assertRaises(OverflowError, os.setgid, 1<<32) + + @unittest.skipUnless(hasattr(os, 'seteuid'), 'test needs os.seteuid()') + def test_seteuid(self): + if os.getuid() != 0: + self.assertRaises(os.error, os.seteuid, 0) + self.assertRaises(OverflowError, os.seteuid, 1<<32) + + @unittest.skipUnless(hasattr(os, 'setegid'), 'test needs os.setegid()') + def test_setegid(self): + if os.getuid() != 0 and not HAVE_WHEEL_GROUP: + self.assertRaises(os.error, os.setegid, 0) + self.assertRaises(OverflowError, os.setegid, 1<<32) + + @unittest.skipUnless(hasattr(os, 'setreuid'), 'test needs os.setreuid()') + def test_setreuid(self): + if os.getuid() != 0: + self.assertRaises(os.error, os.setreuid, 0, 0) + self.assertRaises(OverflowError, os.setreuid, 1<<32, 0) + self.assertRaises(OverflowError, os.setreuid, 0, 1<<32) + + @unittest.skipUnless(hasattr(os, 'setregid'), 'test needs os.setregid()') + def test_setregid(self): + if os.getuid() != 0 and not HAVE_WHEEL_GROUP: + self.assertRaises(os.error, os.setregid, 0, 0) + self.assertRaises(OverflowError, os.setregid, 1<<32, 0) + self.assertRaises(OverflowError, os.setregid, 0, 1<<32) + +@unittest.skipIf(sys.platform == "win32", "Posix specific tests") +class Pep383Tests(unittest.TestCase): + def setUp(self): + if support.TESTFN_UNENCODABLE: + self.dir = support.TESTFN_UNENCODABLE + elif support.TESTFN_NONASCII: + self.dir = support.TESTFN_NONASCII + else: + self.dir = support.TESTFN + self.bdir = os.fsencode(self.dir) - def tearDown(self): + bytesfn = [] + def add_filename(fn): + try: + fn = os.fsencode(fn) + except UnicodeEncodeError: + return + bytesfn.append(fn) + add_filename(support.TESTFN_UNICODE) + if support.TESTFN_UNENCODABLE: + add_filename(support.TESTFN_UNENCODABLE) + if support.TESTFN_NONASCII: + add_filename(support.TESTFN_NONASCII) + if not bytesfn: + self.skipTest("couldn't create any non-ascii filename") + + self.unicodefn = set() + os.mkdir(self.dir) + try: + for fn in bytesfn: + support.create_empty_file(os.path.join(self.bdir, fn)) + fn = os.fsdecode(fn) + if fn in self.unicodefn: + raise ValueError("duplicate filename") + self.unicodefn.add(fn) + except: shutil.rmtree(self.dir) + raise - def test_listdir(self): - expected = self.unicodefn - found = set(os.listdir(self.dir)) - self.assertEqual(found, expected) - # test listdir without arguments - current_directory = os.getcwd() - try: - os.chdir(os.sep) - self.assertEqual(set(os.listdir()), set(os.listdir(os.sep))) - finally: - os.chdir(current_directory) - - def test_open(self): - for fn in self.unicodefn: - f = open(os.path.join(self.dir, fn), 'rb') - f.close() - - @unittest.skipUnless(hasattr(os, 'statvfs'), - "need os.statvfs()") - def test_statvfs(self): - # issue #9645 - for fn in self.unicodefn: - # should not fail with file not found error - fullname = os.path.join(self.dir, fn) - os.statvfs(fullname) - - def test_stat(self): - for fn in self.unicodefn: - os.stat(os.path.join(self.dir, fn)) -else: - class PosixUidGidTests(unittest.TestCase): - pass - class Pep383Tests(unittest.TestCase): - pass + def tearDown(self): + shutil.rmtree(self.dir) + + def test_listdir(self): + expected = self.unicodefn + found = set(os.listdir(self.dir)) + self.assertEqual(found, expected) + # test listdir without arguments + current_directory = os.getcwd() + try: + os.chdir(os.sep) + self.assertEqual(set(os.listdir()), set(os.listdir(os.sep))) + finally: + os.chdir(current_directory) + + def test_open(self): + for fn in self.unicodefn: + f = open(os.path.join(self.dir, fn), 'rb') + f.close() + + @unittest.skipUnless(hasattr(os, 'statvfs'), + "need os.statvfs()") + def test_statvfs(self): + # issue #9645 + for fn in self.unicodefn: + # should not fail with file not found error + fullname = os.path.join(self.dir, fn) + os.statvfs(fullname) + + def test_stat(self): + for fn in self.unicodefn: + os.stat(os.path.join(self.dir, fn)) @unittest.skipUnless(sys.platform == "win32", "Win32 specific tests") class Win32KillTests(unittest.TestCase): @@ -1838,6 +1821,8 @@ class TestSendfile(unittest.TestCase): SUPPORT_HEADERS_TRAILERS = not sys.platform.startswith("linux") and \ not sys.platform.startswith("solaris") and \ not sys.platform.startswith("sunos") + requires_headers_trailers = unittest.skipUnless(SUPPORT_HEADERS_TRAILERS, + 'requires headers and trailers support') @classmethod def setUpClass(cls): @@ -1956,52 +1941,54 @@ class TestSendfile(unittest.TestCase): # --- headers / trailers tests - if SUPPORT_HEADERS_TRAILERS: - - def test_headers(self): - total_sent = 0 - sent = os.sendfile(self.sockno, self.fileno, 0, 4096, - headers=[b"x" * 512]) + @requires_headers_trailers + def test_headers(self): + total_sent = 0 + sent = os.sendfile(self.sockno, self.fileno, 0, 4096, + headers=[b"x" * 512]) + total_sent += sent + offset = 4096 + nbytes = 4096 + while 1: + sent = self.sendfile_wrapper(self.sockno, self.fileno, + offset, nbytes) + if sent == 0: + break total_sent += sent - offset = 4096 - nbytes = 4096 - while 1: - sent = self.sendfile_wrapper(self.sockno, self.fileno, - offset, nbytes) - if sent == 0: - break - total_sent += sent - offset += sent + offset += sent - expected_data = b"x" * 512 + self.DATA - self.assertEqual(total_sent, len(expected_data)) + expected_data = b"x" * 512 + self.DATA + self.assertEqual(total_sent, len(expected_data)) + self.client.close() + self.server.wait() + data = self.server.handler_instance.get_data() + self.assertEqual(hash(data), hash(expected_data)) + + @requires_headers_trailers + def test_trailers(self): + TESTFN2 = support.TESTFN + "2" + file_data = b"abcdef" + with open(TESTFN2, 'wb') as f: + f.write(file_data) + with open(TESTFN2, 'rb')as f: + self.addCleanup(os.remove, TESTFN2) + os.sendfile(self.sockno, f.fileno(), 0, len(file_data), + trailers=[b"1234"]) self.client.close() self.server.wait() data = self.server.handler_instance.get_data() - self.assertEqual(hash(data), hash(expected_data)) - - def test_trailers(self): - TESTFN2 = support.TESTFN + "2" - file_data = b"abcdef" - with open(TESTFN2, 'wb') as f: - f.write(file_data) - with open(TESTFN2, 'rb')as f: - self.addCleanup(os.remove, TESTFN2) - os.sendfile(self.sockno, f.fileno(), 0, len(file_data), - trailers=[b"1234"]) - self.client.close() - self.server.wait() - data = self.server.handler_instance.get_data() - self.assertEqual(data, b"abcdef1234") - - if hasattr(os, "SF_NODISKIO"): - def test_flags(self): - try: - os.sendfile(self.sockno, self.fileno, 0, 4096, - flags=os.SF_NODISKIO) - except OSError as err: - if err.errno not in (errno.EBUSY, errno.EAGAIN): - raise + self.assertEqual(data, b"abcdef1234") + + @requires_headers_trailers + @unittest.skipUnless(hasattr(os, 'SF_NODISKIO'), + 'test needs os.SF_NODISKIO') + def test_flags(self): + try: + os.sendfile(self.sockno, self.fileno, 0, 4096, + flags=os.SF_NODISKIO) + except OSError as err: + if err.errno not in (errno.EBUSY, errno.EAGAIN): + raise def supports_extended_attributes(): diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py index 28e6a6bfc7..cbce8524eb 100644 --- a/Lib/test/test_poplib.py +++ b/Lib/test/test_poplib.py @@ -11,7 +11,7 @@ import os import time import errno -from unittest import TestCase +from unittest import TestCase, skipUnless from test import support as test_support threading = test_support.import_module('threading') @@ -288,35 +288,37 @@ if hasattr(poplib, 'POP3_SSL'): else: DummyPOP3Handler.handle_read(self) +requires_ssl = skipUnless(SUPPORTS_SSL, 'SSL not supported') - class TestPOP3_SSLClass(TestPOP3Class): - # repeat previous tests by using poplib.POP3_SSL +@requires_ssl +class TestPOP3_SSLClass(TestPOP3Class): + # repeat previous tests by using poplib.POP3_SSL - def setUp(self): - self.server = DummyPOP3Server((HOST, PORT)) - self.server.handler = DummyPOP3_SSLHandler - self.server.start() - self.client = poplib.POP3_SSL(self.server.host, self.server.port) + def setUp(self): + self.server = DummyPOP3Server((HOST, PORT)) + self.server.handler = DummyPOP3_SSLHandler + self.server.start() + self.client = poplib.POP3_SSL(self.server.host, self.server.port) - def test__all__(self): - self.assertIn('POP3_SSL', poplib.__all__) + def test__all__(self): + self.assertIn('POP3_SSL', poplib.__all__) - def test_context(self): - ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host, - self.server.port, keyfile=CERTFILE, context=ctx) - self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host, - self.server.port, certfile=CERTFILE, context=ctx) - self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host, - self.server.port, keyfile=CERTFILE, - certfile=CERTFILE, context=ctx) + def test_context(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host, + self.server.port, keyfile=CERTFILE, context=ctx) + self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host, + self.server.port, certfile=CERTFILE, context=ctx) + self.assertRaises(ValueError, poplib.POP3_SSL, self.server.host, + self.server.port, keyfile=CERTFILE, + certfile=CERTFILE, context=ctx) - self.client.quit() - self.client = poplib.POP3_SSL(self.server.host, self.server.port, - context=ctx) - self.assertIsInstance(self.client.sock, ssl.SSLSocket) - self.assertIs(self.client.sock.context, ctx) - self.assertTrue(self.client.noop().startswith(b'+OK')) + self.client.quit() + self.client = poplib.POP3_SSL(self.server.host, self.server.port, + context=ctx) + self.assertIsInstance(self.client.sock, ssl.SSLSocket) + self.assertIs(self.client.sock.context, ctx) + self.assertTrue(self.client.noop().startswith(b'+OK')) class TestTimeouts(TestCase): @@ -374,9 +376,8 @@ class TestTimeouts(TestCase): def test_main(): - tests = [TestPOP3Class, TestTimeouts] - if SUPPORTS_SSL: - tests.append(TestPOP3_SSLClass) + tests = [TestPOP3Class, TestTimeouts, + TestPOP3_SSLClass] thread_info = test_support.threading_setup() try: test_support.run_unittest(*tests) diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 4d9e1f599b..02bb6acb82 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -54,47 +54,55 @@ class PosixTester(unittest.TestCase): posix_func() self.assertRaises(TypeError, posix_func, 1) - if hasattr(posix, 'getresuid'): - def test_getresuid(self): - user_ids = posix.getresuid() - self.assertEqual(len(user_ids), 3) - for val in user_ids: - self.assertGreaterEqual(val, 0) - - if hasattr(posix, 'getresgid'): - def test_getresgid(self): - group_ids = posix.getresgid() - self.assertEqual(len(group_ids), 3) - for val in group_ids: - self.assertGreaterEqual(val, 0) - - if hasattr(posix, 'setresuid'): - def test_setresuid(self): - current_user_ids = posix.getresuid() - self.assertIsNone(posix.setresuid(*current_user_ids)) - # -1 means don't change that value. - self.assertIsNone(posix.setresuid(-1, -1, -1)) - - def test_setresuid_exception(self): - # Don't do this test if someone is silly enough to run us as root. - current_user_ids = posix.getresuid() - if 0 not in current_user_ids: - new_user_ids = (current_user_ids[0]+1, -1, -1) - self.assertRaises(OSError, posix.setresuid, *new_user_ids) - - if hasattr(posix, 'setresgid'): - def test_setresgid(self): - current_group_ids = posix.getresgid() - self.assertIsNone(posix.setresgid(*current_group_ids)) - # -1 means don't change that value. - self.assertIsNone(posix.setresgid(-1, -1, -1)) - - def test_setresgid_exception(self): - # Don't do this test if someone is silly enough to run us as root. - current_group_ids = posix.getresgid() - if 0 not in current_group_ids: - new_group_ids = (current_group_ids[0]+1, -1, -1) - self.assertRaises(OSError, posix.setresgid, *new_group_ids) + @unittest.skipUnless(hasattr(posix, 'getresuid'), + 'test needs posix.getresuid()') + def test_getresuid(self): + user_ids = posix.getresuid() + self.assertEqual(len(user_ids), 3) + for val in user_ids: + self.assertGreaterEqual(val, 0) + + @unittest.skipUnless(hasattr(posix, 'getresgid'), + 'test needs posix.getresgid()') + def test_getresgid(self): + group_ids = posix.getresgid() + self.assertEqual(len(group_ids), 3) + for val in group_ids: + self.assertGreaterEqual(val, 0) + + @unittest.skipUnless(hasattr(posix, 'setresuid'), + 'test needs posix.setresuid()') + def test_setresuid(self): + current_user_ids = posix.getresuid() + self.assertIsNone(posix.setresuid(*current_user_ids)) + # -1 means don't change that value. + self.assertIsNone(posix.setresuid(-1, -1, -1)) + + @unittest.skipUnless(hasattr(posix, 'setresuid'), + 'test needs posix.setresuid()') + def test_setresuid_exception(self): + # Don't do this test if someone is silly enough to run us as root. + current_user_ids = posix.getresuid() + if 0 not in current_user_ids: + new_user_ids = (current_user_ids[0]+1, -1, -1) + self.assertRaises(OSError, posix.setresuid, *new_user_ids) + + @unittest.skipUnless(hasattr(posix, 'setresgid'), + 'test needs posix.setresgid()') + def test_setresgid(self): + current_group_ids = posix.getresgid() + self.assertIsNone(posix.setresgid(*current_group_ids)) + # -1 means don't change that value. + self.assertIsNone(posix.setresgid(-1, -1, -1)) + + @unittest.skipUnless(hasattr(posix, 'setresgid'), + 'test needs posix.setresgid()') + def test_setresgid_exception(self): + # Don't do this test if someone is silly enough to run us as root. + current_group_ids = posix.getresgid() + if 0 not in current_group_ids: + new_group_ids = (current_group_ids[0]+1, -1, -1) + self.assertRaises(OSError, posix.setresgid, *new_group_ids) @unittest.skipUnless(hasattr(posix, 'initgroups'), "test needs os.initgroups()") @@ -121,29 +129,32 @@ class PosixTester(unittest.TestCase): else: self.fail("Expected OSError to be raised by initgroups") + @unittest.skipUnless(hasattr(posix, 'statvfs'), + 'test needs posix.statvfs()') def test_statvfs(self): - if hasattr(posix, 'statvfs'): - self.assertTrue(posix.statvfs(os.curdir)) + self.assertTrue(posix.statvfs(os.curdir)) + @unittest.skipUnless(hasattr(posix, 'fstatvfs'), + 'test needs posix.fstatvfs()') def test_fstatvfs(self): - if hasattr(posix, 'fstatvfs'): - fp = open(support.TESTFN) - try: - self.assertTrue(posix.fstatvfs(fp.fileno())) - self.assertTrue(posix.statvfs(fp.fileno())) - finally: - fp.close() + fp = open(support.TESTFN) + try: + self.assertTrue(posix.fstatvfs(fp.fileno())) + self.assertTrue(posix.statvfs(fp.fileno())) + finally: + fp.close() + @unittest.skipUnless(hasattr(posix, 'ftruncate'), + 'test needs posix.ftruncate()') def test_ftruncate(self): - if hasattr(posix, 'ftruncate'): - fp = open(support.TESTFN, 'w+') - try: - # we need to have some data to truncate - fp.write('test') - fp.flush() - posix.ftruncate(fp.fileno(), 0) - finally: - fp.close() + fp = open(support.TESTFN, 'w+') + try: + # we need to have some data to truncate + fp.write('test') + fp.flush() + posix.ftruncate(fp.fileno(), 0) + finally: + fp.close() @unittest.skipUnless(hasattr(posix, 'truncate'), "test needs posix.truncate()") def test_truncate(self): @@ -290,30 +301,33 @@ class PosixTester(unittest.TestCase): finally: os.close(fd) + @unittest.skipUnless(hasattr(posix, 'dup'), + 'test needs posix.dup()') def test_dup(self): - if hasattr(posix, 'dup'): - fp = open(support.TESTFN) - try: - fd = posix.dup(fp.fileno()) - self.assertIsInstance(fd, int) - os.close(fd) - finally: - fp.close() + fp = open(support.TESTFN) + try: + fd = posix.dup(fp.fileno()) + self.assertIsInstance(fd, int) + os.close(fd) + finally: + fp.close() + @unittest.skipUnless(hasattr(posix, 'confstr'), + 'test needs posix.confstr()') def test_confstr(self): - if hasattr(posix, 'confstr'): - self.assertRaises(ValueError, posix.confstr, "CS_garbage") - self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True) + self.assertRaises(ValueError, posix.confstr, "CS_garbage") + self.assertEqual(len(posix.confstr("CS_PATH")) > 0, True) + @unittest.skipUnless(hasattr(posix, 'dup2'), + 'test needs posix.dup2()') def test_dup2(self): - if hasattr(posix, 'dup2'): - fp1 = open(support.TESTFN) - fp2 = open(support.TESTFN) - try: - posix.dup2(fp1.fileno(), fp2.fileno()) - finally: - fp1.close() - fp2.close() + fp1 = open(support.TESTFN) + fp2 = open(support.TESTFN) + try: + posix.dup2(fp1.fileno(), fp2.fileno()) + finally: + fp1.close() + fp2.close() @unittest.skipUnless(hasattr(os, 'O_CLOEXEC'), "needs os.O_CLOEXEC") @support.requires_linux_version(2, 6, 23) @@ -322,65 +336,69 @@ class PosixTester(unittest.TestCase): self.addCleanup(os.close, fd) self.assertTrue(fcntl.fcntl(fd, fcntl.F_GETFD) & fcntl.FD_CLOEXEC) + @unittest.skipUnless(hasattr(posix, 'O_EXLOCK'), + 'test needs posix.O_EXLOCK') def test_osexlock(self): - if hasattr(posix, "O_EXLOCK"): + fd = os.open(support.TESTFN, + os.O_WRONLY|os.O_EXLOCK|os.O_CREAT) + self.assertRaises(OSError, os.open, support.TESTFN, + os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK) + os.close(fd) + + if hasattr(posix, "O_SHLOCK"): fd = os.open(support.TESTFN, - os.O_WRONLY|os.O_EXLOCK|os.O_CREAT) + os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) self.assertRaises(OSError, os.open, support.TESTFN, os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK) os.close(fd) - if hasattr(posix, "O_SHLOCK"): - fd = os.open(support.TESTFN, - os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, support.TESTFN, - os.O_WRONLY|os.O_EXLOCK|os.O_NONBLOCK) - os.close(fd) - + @unittest.skipUnless(hasattr(posix, 'O_SHLOCK'), + 'test needs posix.O_SHLOCK') def test_osshlock(self): - if hasattr(posix, "O_SHLOCK"): - fd1 = os.open(support.TESTFN, + fd1 = os.open(support.TESTFN, + os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) + fd2 = os.open(support.TESTFN, + os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) + os.close(fd2) + os.close(fd1) + + if hasattr(posix, "O_EXLOCK"): + fd = os.open(support.TESTFN, os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - fd2 = os.open(support.TESTFN, - os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - os.close(fd2) - os.close(fd1) - - if hasattr(posix, "O_EXLOCK"): - fd = os.open(support.TESTFN, - os.O_WRONLY|os.O_SHLOCK|os.O_CREAT) - self.assertRaises(OSError, os.open, support.TESTFN, - os.O_RDONLY|os.O_EXLOCK|os.O_NONBLOCK) - os.close(fd) + self.assertRaises(OSError, os.open, support.TESTFN, + os.O_RDONLY|os.O_EXLOCK|os.O_NONBLOCK) + os.close(fd) + @unittest.skipUnless(hasattr(posix, 'fstat'), + 'test needs posix.fstat()') def test_fstat(self): - if hasattr(posix, 'fstat'): - fp = open(support.TESTFN) - try: - self.assertTrue(posix.fstat(fp.fileno())) - self.assertTrue(posix.stat(fp.fileno())) - - self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', - posix.stat, float(fp.fileno())) - finally: - fp.close() - - def test_stat(self): - if hasattr(posix, 'stat'): - self.assertTrue(posix.stat(support.TESTFN)) - self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) - self.assertTrue(posix.stat(bytearray(os.fsencode(support.TESTFN)))) + fp = open(support.TESTFN) + try: + self.assertTrue(posix.fstat(fp.fileno())) + self.assertTrue(posix.stat(fp.fileno())) self.assertRaisesRegex(TypeError, - 'can\'t specify None for path argument', - posix.stat, None) - self.assertRaisesRegex(TypeError, - 'should be string, bytes or integer, not', - posix.stat, list(support.TESTFN)) - self.assertRaisesRegex(TypeError, 'should be string, bytes or integer, not', - posix.stat, list(os.fsencode(support.TESTFN))) + posix.stat, float(fp.fileno())) + finally: + fp.close() + + @unittest.skipUnless(hasattr(posix, 'stat'), + 'test needs posix.stat()') + def test_stat(self): + self.assertTrue(posix.stat(support.TESTFN)) + self.assertTrue(posix.stat(os.fsencode(support.TESTFN))) + self.assertTrue(posix.stat(bytearray(os.fsencode(support.TESTFN)))) + + self.assertRaisesRegex(TypeError, + 'can\'t specify None for path argument', + posix.stat, None) + self.assertRaisesRegex(TypeError, + 'should be string, bytes or integer, not', + posix.stat, list(support.TESTFN)) + self.assertRaisesRegex(TypeError, + 'should be string, bytes or integer, not', + posix.stat, list(os.fsencode(support.TESTFN))) @unittest.skipUnless(hasattr(posix, 'mkfifo'), "don't have mkfifo()") def test_mkfifo(self): @@ -495,10 +513,10 @@ class PosixTester(unittest.TestCase): self._test_all_chown_common(posix.lchown, support.TESTFN, getattr(posix, 'lstat', None)) + @unittest.skipUnless(hasattr(posix, 'chdir'), 'test needs posix.chdir()') def test_chdir(self): - if hasattr(posix, 'chdir'): - posix.chdir(os.curdir) - self.assertRaises(OSError, posix.chdir, support.TESTFN) + posix.chdir(os.curdir) + self.assertRaises(OSError, posix.chdir, support.TESTFN) def test_listdir(self): self.assertTrue(support.TESTFN in posix.listdir(os.curdir)) @@ -528,25 +546,26 @@ class PosixTester(unittest.TestCase): sorted(posix.listdir(f)) ) + @unittest.skipUnless(hasattr(posix, 'access'), 'test needs posix.access()') def test_access(self): - if hasattr(posix, 'access'): - self.assertTrue(posix.access(support.TESTFN, os.R_OK)) + self.assertTrue(posix.access(support.TESTFN, os.R_OK)) + @unittest.skipUnless(hasattr(posix, 'umask'), 'test needs posix.umask()') def test_umask(self): - if hasattr(posix, 'umask'): - old_mask = posix.umask(0) - self.assertIsInstance(old_mask, int) - posix.umask(old_mask) + old_mask = posix.umask(0) + self.assertIsInstance(old_mask, int) + posix.umask(old_mask) + @unittest.skipUnless(hasattr(posix, 'strerror'), + 'test needs posix.strerror()') def test_strerror(self): - if hasattr(posix, 'strerror'): - self.assertTrue(posix.strerror(0)) + self.assertTrue(posix.strerror(0)) + @unittest.skipUnless(hasattr(posix, 'pipe'), 'test needs posix.pipe()') def test_pipe(self): - if hasattr(posix, 'pipe'): - reader, writer = posix.pipe() - os.close(reader) - os.close(writer) + reader, writer = posix.pipe() + os.close(reader) + os.close(writer) @unittest.skipUnless(hasattr(os, 'pipe2'), "test needs os.pipe2()") @support.requires_linux_version(2, 6, 27) @@ -578,15 +597,15 @@ class PosixTester(unittest.TestCase): self.assertRaises(OverflowError, os.pipe2, _testcapi.INT_MAX + 1) self.assertRaises(OverflowError, os.pipe2, _testcapi.UINT_MAX + 1) + @unittest.skipUnless(hasattr(posix, 'utime'), 'test needs posix.utime()') def test_utime(self): - if hasattr(posix, 'utime'): - now = time.time() - posix.utime(support.TESTFN, None) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None)) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None)) - self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now)) - posix.utime(support.TESTFN, (int(now), int(now))) - posix.utime(support.TESTFN, (now, now)) + now = time.time() + posix.utime(support.TESTFN, None) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, None)) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (now, None)) + self.assertRaises(TypeError, posix.utime, support.TESTFN, (None, now)) + posix.utime(support.TESTFN, (int(now), int(now))) + posix.utime(support.TESTFN, (now, now)) def _test_chflags_regular_file(self, chflags_func, target_file, **kwargs): st = os.stat(target_file) @@ -663,6 +682,7 @@ class PosixTester(unittest.TestCase): self.assertEqual(type(k), item_type) self.assertEqual(type(v), item_type) + @unittest.skipUnless(hasattr(posix, 'getcwd'), 'test needs posix.getcwd()') def test_getcwd_long_pathnames(self): if hasattr(posix, 'getcwd'): dirname = 'getcwd-test-directory-0123456789abcdef-01234567890abcdef' diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py index cdcc707d61..43f4163dae 100644 --- a/Lib/test/test_pydoc.py +++ b/Lib/test/test_pydoc.py @@ -404,7 +404,7 @@ class PydocDocTest(unittest.TestCase): def test_namedtuple_public_underscore(self): NT = namedtuple('NT', ['abc', 'def'], rename=True) with captured_stdout() as help_io: - help(NT) + pydoc.help(NT) helptext = help_io.getvalue() self.assertIn('_1', helptext) self.assertIn('_replace', helptext) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index d02e42759a..c0bca2fb88 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -625,10 +625,10 @@ class TestSet(TestJointOps, unittest.TestCase): myset >= myobj self.assertTrue(myobj.le_called) - # C API test only available in a debug build - if hasattr(set, "test_c_api"): - def test_c_api(self): - self.assertEqual(set().test_c_api(), True) + @unittest.skipUnless(hasattr(set, "test_c_api"), + 'C API test only available in a debug build') + def test_c_api(self): + self.assertEqual(set().test_c_api(), True) class SetSubclass(set): pass diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py index 9af7da71cb..df95bd9a5c 100644 --- a/Lib/test/test_shutil.py +++ b/Lib/test/test_shutil.py @@ -194,37 +194,37 @@ class TestShutil(unittest.TestCase): self.assertIn(errors[1][2][1].filename, possible_args) - # See bug #1071513 for why we don't run this on cygwin - # and bug #1076467 for why we don't run this as root. - if (hasattr(os, 'chmod') and sys.platform[:6] != 'cygwin' - and not (hasattr(os, 'geteuid') and os.geteuid() == 0)): - def test_on_error(self): - self.errorState = 0 - os.mkdir(TESTFN) - self.addCleanup(shutil.rmtree, TESTFN) - - self.child_file_path = os.path.join(TESTFN, 'a') - self.child_dir_path = os.path.join(TESTFN, 'b') - support.create_empty_file(self.child_file_path) - os.mkdir(self.child_dir_path) - old_dir_mode = os.stat(TESTFN).st_mode - old_child_file_mode = os.stat(self.child_file_path).st_mode - old_child_dir_mode = os.stat(self.child_dir_path).st_mode - # Make unwritable. - new_mode = stat.S_IREAD|stat.S_IEXEC - os.chmod(self.child_file_path, new_mode) - os.chmod(self.child_dir_path, new_mode) - os.chmod(TESTFN, new_mode) - - self.addCleanup(os.chmod, TESTFN, old_dir_mode) - self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode) - self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode) - - shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) - # Test whether onerror has actually been called. - self.assertEqual(self.errorState, 3, - "Expected call to onerror function did not " - "happen.") + @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod()') + @unittest.skipIf(sys.platform[:6] == 'cygwin', + "This test can't be run on Cygwin (issue #1071513).") + @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0, + "This test can't be run reliably as root (issue #1076467).") + def test_on_error(self): + self.errorState = 0 + os.mkdir(TESTFN) + self.addCleanup(shutil.rmtree, TESTFN) + + self.child_file_path = os.path.join(TESTFN, 'a') + self.child_dir_path = os.path.join(TESTFN, 'b') + support.create_empty_file(self.child_file_path) + os.mkdir(self.child_dir_path) + old_dir_mode = os.stat(TESTFN).st_mode + old_child_file_mode = os.stat(self.child_file_path).st_mode + old_child_dir_mode = os.stat(self.child_dir_path).st_mode + # Make unwritable. + new_mode = stat.S_IREAD|stat.S_IEXEC + os.chmod(self.child_file_path, new_mode) + os.chmod(self.child_dir_path, new_mode) + os.chmod(TESTFN, new_mode) + + self.addCleanup(os.chmod, TESTFN, old_dir_mode) + self.addCleanup(os.chmod, self.child_file_path, old_child_file_mode) + self.addCleanup(os.chmod, self.child_dir_path, old_child_dir_mode) + + shutil.rmtree(TESTFN, onerror=self.check_args_to_onerror) + # Test whether onerror has actually been called. + self.assertEqual(self.errorState, 3, + "Expected call to onerror function did not happen.") def check_args_to_onerror(self, func, arg, exc): # test_rmtree_errors deliberately runs rmtree @@ -806,38 +806,39 @@ class TestShutil(unittest.TestCase): finally: shutil.rmtree(TESTFN, ignore_errors=True) - if hasattr(os, "mkfifo"): - # Issue #3002: copyfile and copytree block indefinitely on named pipes - def test_copyfile_named_pipe(self): - os.mkfifo(TESTFN) - try: - self.assertRaises(shutil.SpecialFileError, - shutil.copyfile, TESTFN, TESTFN2) - self.assertRaises(shutil.SpecialFileError, - shutil.copyfile, __file__, TESTFN) - finally: - os.remove(TESTFN) + # Issue #3002: copyfile and copytree block indefinitely on named pipes + @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') + def test_copyfile_named_pipe(self): + os.mkfifo(TESTFN) + try: + self.assertRaises(shutil.SpecialFileError, + shutil.copyfile, TESTFN, TESTFN2) + self.assertRaises(shutil.SpecialFileError, + shutil.copyfile, __file__, TESTFN) + finally: + os.remove(TESTFN) - @support.skip_unless_symlink - def test_copytree_named_pipe(self): - os.mkdir(TESTFN) + @unittest.skipUnless(hasattr(os, "mkfifo"), 'requires os.mkfifo()') + @support.skip_unless_symlink + def test_copytree_named_pipe(self): + os.mkdir(TESTFN) + try: + subdir = os.path.join(TESTFN, "subdir") + os.mkdir(subdir) + pipe = os.path.join(subdir, "mypipe") + os.mkfifo(pipe) try: - subdir = os.path.join(TESTFN, "subdir") - os.mkdir(subdir) - pipe = os.path.join(subdir, "mypipe") - os.mkfifo(pipe) - try: - shutil.copytree(TESTFN, TESTFN2) - except shutil.Error as e: - errors = e.args[0] - self.assertEqual(len(errors), 1) - src, dst, error_msg = errors[0] - self.assertEqual("`%s` is a named pipe" % pipe, error_msg) - else: - self.fail("shutil.Error should have been raised") - finally: - shutil.rmtree(TESTFN, ignore_errors=True) - shutil.rmtree(TESTFN2, ignore_errors=True) + shutil.copytree(TESTFN, TESTFN2) + except shutil.Error as e: + errors = e.args[0] + self.assertEqual(len(errors), 1) + src, dst, error_msg = errors[0] + self.assertEqual("`%s` is a named pipe" % pipe, error_msg) + else: + self.fail("shutil.Error should have been raised") + finally: + shutil.rmtree(TESTFN, ignore_errors=True) + shutil.rmtree(TESTFN2, ignore_errors=True) def test_copytree_special_func(self): diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py index b95e1abafe..12517ae95d 100644 --- a/Lib/test/test_socket.py +++ b/Lib/test/test_socket.py @@ -772,16 +772,17 @@ class GeneralModuleTests(unittest.TestCase): self.assertRaises(TypeError, socket.if_nametoindex, 0) self.assertRaises(TypeError, socket.if_indextoname, '_DEADBEEF') + @unittest.skipUnless(hasattr(sys, 'getrefcount'), + 'test needs sys.getrefcount()') def testRefCountGetNameInfo(self): # Testing reference count for getnameinfo - if hasattr(sys, "getrefcount"): - try: - # On some versions, this loses a reference - orig = sys.getrefcount(__name__) - socket.getnameinfo(__name__,0) - except TypeError: - if sys.getrefcount(__name__) != orig: - self.fail("socket.getnameinfo loses a reference") + try: + # On some versions, this loses a reference + orig = sys.getrefcount(__name__) + socket.getnameinfo(__name__,0) + except TypeError: + if sys.getrefcount(__name__) != orig: + self.fail("socket.getnameinfo loses a reference") def testInterpreterCrash(self): # Making sure getnameinfo doesn't crash the interpreter @@ -886,17 +887,17 @@ class GeneralModuleTests(unittest.TestCase): # Check that setting it to an invalid type raises TypeError self.assertRaises(TypeError, socket.setdefaulttimeout, "spam") + @unittest.skipUnless(hasattr(socket, 'inet_aton'), + 'test needs socket.inet_aton()') def testIPv4_inet_aton_fourbytes(self): - if not hasattr(socket, 'inet_aton'): - return # No inet_aton, nothing to check # Test that issue1008086 and issue767150 are fixed. # It must return 4 bytes. self.assertEqual(b'\x00'*4, socket.inet_aton('0.0.0.0')) self.assertEqual(b'\xff'*4, socket.inet_aton('255.255.255.255')) + @unittest.skipUnless(hasattr(socket, 'inet_pton'), + 'test needs socket.inet_pton()') def testIPv4toString(self): - if not hasattr(socket, 'inet_pton'): - return # No inet_pton() on this platform from socket import inet_aton as f, inet_pton, AF_INET g = lambda a: inet_pton(AF_INET, a) @@ -925,9 +926,9 @@ class GeneralModuleTests(unittest.TestCase): assertInvalid(g, '1.2.3.4.5') assertInvalid(g, '::1') + @unittest.skipUnless(hasattr(socket, 'inet_pton'), + 'test needs socket.inet_pton()') def testIPv6toString(self): - if not hasattr(socket, 'inet_pton'): - return # No inet_pton() on this platform try: from socket import inet_pton, AF_INET6, has_ipv6 if not has_ipv6: @@ -979,9 +980,9 @@ class GeneralModuleTests(unittest.TestCase): assertInvalid('::1.2.3.4:0') assertInvalid('0.100.200.0:3:4:5:6:7:8') + @unittest.skipUnless(hasattr(socket, 'inet_ntop'), + 'test needs socket.inet_ntop()') def testStringToIPv4(self): - if not hasattr(socket, 'inet_ntop'): - return # No inet_ntop() on this platform from socket import inet_ntoa as f, inet_ntop, AF_INET g = lambda a: inet_ntop(AF_INET, a) assertInvalid = lambda func,a: self.assertRaises( @@ -1003,9 +1004,9 @@ class GeneralModuleTests(unittest.TestCase): assertInvalid(g, b'\x00' * 5) assertInvalid(g, b'\x00' * 16) + @unittest.skipUnless(hasattr(socket, 'inet_ntop'), + 'test needs socket.inet_ntop()') def testStringToIPv6(self): - if not hasattr(socket, 'inet_ntop'): - return # No inet_ntop() on this platform try: from socket import inet_ntop, AF_INET6, has_ipv6 if not has_ipv6: @@ -3531,6 +3532,8 @@ class TCPCloserTest(ThreadedTCPSocketTest): self.cli.connect((HOST, self.port)) time.sleep(1.0) +@unittest.skipUnless(hasattr(socket, 'socketpair'), + 'test needs socket.socketpair()') @unittest.skipUnless(thread, 'Threading required for this test.') class BasicSocketPairTest(SocketPairTest): @@ -3593,26 +3596,27 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest): def _testSetBlocking(self): pass - if hasattr(socket, "SOCK_NONBLOCK"): - @support.requires_linux_version(2, 6, 28) - def testInitNonBlocking(self): - # reinit server socket - self.serv.close() - self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM | - socket.SOCK_NONBLOCK) - self.port = support.bind_port(self.serv) - self.serv.listen(1) - # actual testing - start = time.time() - try: - self.serv.accept() - except socket.error: - pass - end = time.time() - self.assertTrue((end - start) < 1.0, "Error creating with non-blocking mode.") - - def _testInitNonBlocking(self): + @unittest.skipUnless(hasattr(socket, 'SOCK_NONBLOCK'), + 'test needs socket.SOCK_NONBLOCK') + @support.requires_linux_version(2, 6, 28) + def testInitNonBlocking(self): + # reinit server socket + self.serv.close() + self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM | + socket.SOCK_NONBLOCK) + self.port = support.bind_port(self.serv) + self.serv.listen(1) + # actual testing + start = time.time() + try: + self.serv.accept() + except socket.error: pass + end = time.time() + self.assertTrue((end - start) < 1.0, "Error creating with non-blocking mode.") + + def _testInitNonBlocking(self): + pass def testInheritFlags(self): # Issue #7995: when calling accept() on a listening socket with a @@ -4302,12 +4306,12 @@ class TCPTimeoutTest(SocketTCPTest): if not ok: self.fail("accept() returned success when we did not expect it") + @unittest.skipUnless(hasattr(signal, 'alarm'), + 'test needs signal.alarm()') def testInterruptedTimeout(self): # XXX I don't know how to do this test on MSWindows or any other # plaform that doesn't support signal.alarm() or os.kill(), though # the bug should have existed on all platforms. - if not hasattr(signal, "alarm"): - return # can only test on *nix self.serv.settimeout(5.0) # must be longer than alarm class Alarm(Exception): pass @@ -4367,6 +4371,7 @@ class TestExceptions(unittest.TestCase): self.assertTrue(issubclass(socket.gaierror, socket.error)) self.assertTrue(issubclass(socket.timeout, socket.error)) +@unittest.skipUnless(sys.platform == 'linux', 'Linux specific test') class TestLinuxAbstractNamespace(unittest.TestCase): UNIX_PATH_MAX = 108 @@ -4402,6 +4407,7 @@ class TestLinuxAbstractNamespace(unittest.TestCase): finally: s.close() +@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'test needs socket.AF_UNIX') class TestUnixDomain(unittest.TestCase): def setUp(self): @@ -4551,10 +4557,10 @@ def isTipcAvailable(): for line in f: if line.startswith("tipc "): return True - if support.verbose: - print("TIPC module is not loaded, please 'sudo modprobe tipc'") return False +@unittest.skipUnless(isTipcAvailable(), + "TIPC module is not loaded, please 'sudo modprobe tipc'") class TIPCTest(unittest.TestCase): def testRDM(self): srv = socket.socket(socket.AF_TIPC, socket.SOCK_RDM) @@ -4577,6 +4583,8 @@ class TIPCTest(unittest.TestCase): self.assertEqual(msg, MSG) +@unittest.skipUnless(isTipcAvailable(), + "TIPC module is not loaded, please 'sudo modprobe tipc'") class TIPCThreadableTest(unittest.TestCase, ThreadableTest): def __init__(self, methodName = 'runTest'): unittest.TestCase.__init__(self, methodName = methodName) @@ -4842,15 +4850,10 @@ def test_main(): CloexecConstantTest, NonblockConstantTest ]) - if hasattr(socket, "socketpair"): - tests.append(BasicSocketPairTest) - if hasattr(socket, "AF_UNIX"): - tests.append(TestUnixDomain) - if sys.platform == 'linux': - tests.append(TestLinuxAbstractNamespace) - if isTipcAvailable(): - tests.append(TIPCTest) - tests.append(TIPCThreadableTest) + tests.append(BasicSocketPairTest) + tests.append(TestUnixDomain) + tests.append(TestLinuxAbstractNamespace) + tests.extend([TIPCTest, TIPCThreadableTest]) tests.extend([BasicCANTest, CANTest]) tests.extend([BasicRDSTest, RDSTest]) tests.extend([ diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 464057e19f..59d8e5dcb7 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -27,7 +27,10 @@ TEST_STR = b"hello world\n" HOST = test.support.HOST HAVE_UNIX_SOCKETS = hasattr(socket, "AF_UNIX") +requires_unix_sockets = unittest.skipUnless(HAVE_UNIX_SOCKETS, + 'requires Unix sockets') HAVE_FORKING = hasattr(os, "fork") and os.name != "os2" +requires_forking = unittest.skipUnless(HAVE_FORKING, 'requires forking') def signal_alarm(n): """Call signal.alarm when it exists (i.e. not on Windows).""" @@ -189,31 +192,33 @@ class SocketServerTest(unittest.TestCase): socketserver.StreamRequestHandler, self.stream_examine) - if HAVE_FORKING: - def test_ForkingTCPServer(self): - with simple_subprocess(self): - self.run_server(socketserver.ForkingTCPServer, - socketserver.StreamRequestHandler, - self.stream_examine) - - if HAVE_UNIX_SOCKETS: - def test_UnixStreamServer(self): - self.run_server(socketserver.UnixStreamServer, + @requires_forking + def test_ForkingTCPServer(self): + with simple_subprocess(self): + self.run_server(socketserver.ForkingTCPServer, socketserver.StreamRequestHandler, self.stream_examine) - def test_ThreadingUnixStreamServer(self): - self.run_server(socketserver.ThreadingUnixStreamServer, + @requires_unix_sockets + def test_UnixStreamServer(self): + self.run_server(socketserver.UnixStreamServer, + socketserver.StreamRequestHandler, + self.stream_examine) + + @requires_unix_sockets + def test_ThreadingUnixStreamServer(self): + self.run_server(socketserver.ThreadingUnixStreamServer, + socketserver.StreamRequestHandler, + self.stream_examine) + + @requires_unix_sockets + @requires_forking + def test_ForkingUnixStreamServer(self): + with simple_subprocess(self): + self.run_server(ForkingUnixStreamServer, socketserver.StreamRequestHandler, self.stream_examine) - if HAVE_FORKING: - def test_ForkingUnixStreamServer(self): - with simple_subprocess(self): - self.run_server(ForkingUnixStreamServer, - socketserver.StreamRequestHandler, - self.stream_examine) - def test_UDPServer(self): self.run_server(socketserver.UDPServer, socketserver.DatagramRequestHandler, @@ -224,12 +229,12 @@ class SocketServerTest(unittest.TestCase): socketserver.DatagramRequestHandler, self.dgram_examine) - if HAVE_FORKING: - def test_ForkingUDPServer(self): - with simple_subprocess(self): - self.run_server(socketserver.ForkingUDPServer, - socketserver.DatagramRequestHandler, - self.dgram_examine) + @requires_forking + def test_ForkingUDPServer(self): + with simple_subprocess(self): + self.run_server(socketserver.ForkingUDPServer, + socketserver.DatagramRequestHandler, + self.dgram_examine) @contextlib.contextmanager def mocked_select_module(self): @@ -266,22 +271,24 @@ class SocketServerTest(unittest.TestCase): # Alas, on Linux (at least) recvfrom() doesn't return a meaningful # client address so this cannot work: - # if HAVE_UNIX_SOCKETS: - # def test_UnixDatagramServer(self): - # self.run_server(socketserver.UnixDatagramServer, - # socketserver.DatagramRequestHandler, - # self.dgram_examine) + # @requires_unix_sockets + # def test_UnixDatagramServer(self): + # self.run_server(socketserver.UnixDatagramServer, + # socketserver.DatagramRequestHandler, + # self.dgram_examine) # - # def test_ThreadingUnixDatagramServer(self): - # self.run_server(socketserver.ThreadingUnixDatagramServer, - # socketserver.DatagramRequestHandler, - # self.dgram_examine) + # @requires_unix_sockets + # def test_ThreadingUnixDatagramServer(self): + # self.run_server(socketserver.ThreadingUnixDatagramServer, + # socketserver.DatagramRequestHandler, + # self.dgram_examine) # - # if HAVE_FORKING: - # def test_ForkingUnixDatagramServer(self): - # self.run_server(socketserver.ForkingUnixDatagramServer, - # socketserver.DatagramRequestHandler, - # self.dgram_examine) + # @requires_unix_sockets + # @requires_forking + # def test_ForkingUnixDatagramServer(self): + # self.run_server(socketserver.ForkingUnixDatagramServer, + # socketserver.DatagramRequestHandler, + # self.dgram_examine) @reap_threads def test_shutdown(self): diff --git a/Lib/test/test_strftime.py b/Lib/test/test_strftime.py index 14057eb34a..78c9c5bdb9 100644 --- a/Lib/test/test_strftime.py +++ b/Lib/test/test_strftime.py @@ -176,8 +176,32 @@ class StrftimeTest(unittest.TestCase): (e[0], e[2])) print(" Expected %s, but got %s" % (e[1], result)) + +class Y1900Tests(unittest.TestCase): + """A limitation of the MS C runtime library is that it crashes if + a date before 1900 is passed with a format string containing "%y" + """ + + @unittest.skipUnless(sys.platform == "win32", "Only applies to Windows") + def test_y_before_1900_win(self): + with self.assertRaises(ValueError): + time.strftime("%y", (1899, 1, 1, 0, 0, 0, 0, 0, 0)) + + @unittest.skipIf(sys.platform == "win32", "Doesn't apply on Windows") + def test_y_before_1900_nonwin(self): + self.assertEquals( + time.strftime("%y", (1899, 1, 1, 0, 0, 0, 0, 0, 0)), "99") + + def test_y_1900(self): + self.assertEquals( + time.strftime("%y", (1900, 1, 1, 0, 0, 0, 0, 0, 0)), "00") + + def test_y_after_1900(self): + self.assertEquals( + time.strftime("%y", (2013, 1, 1, 0, 0, 0, 0, 0, 0)), "13") + def test_main(): - support.run_unittest(StrftimeTest) + support.run_unittest(StrftimeTest, Y1900Tests) if __name__ == '__main__': test_main() diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 4c9f29bdb8..77f1ba3155 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -2133,13 +2133,6 @@ class Win32ProcessTestCase(BaseTestCase): def test_terminate_dead(self): self._kill_dead_process('terminate') - -# The module says: -# "NB This only works (and is only relevant) for UNIX." -# -# Actually, getoutput should work on any platform with an os.popen, but -# I'll take the comment as given, and skip this suite. -@unittest.skipUnless(os.name == 'posix', "only relevant for UNIX") class CommandTests(unittest.TestCase): def test_getoutput(self): self.assertEqual(subprocess.getoutput('echo xyzzy'), 'xyzzy') @@ -2153,8 +2146,8 @@ class CommandTests(unittest.TestCase): try: dir = tempfile.mkdtemp() name = os.path.join(dir, "foo") - - status, output = subprocess.getstatusoutput('cat ' + name) + status, output = subprocess.getstatusoutput( + ("type " if mswindows else "cat ") + name) self.assertNotEqual(status, 0) finally: if dir is not None: diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index bc4c0ad46d..acf6d364f2 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -291,15 +291,16 @@ class SysModuleTest(unittest.TestCase): def test_call_tracing(self): self.assertRaises(TypeError, sys.call_tracing, type, 2) + @unittest.skipUnless(hasattr(sys, "setdlopenflags"), + 'test needs sys.setdlopenflags()') def test_dlopenflags(self): - if hasattr(sys, "setdlopenflags"): - self.assertTrue(hasattr(sys, "getdlopenflags")) - self.assertRaises(TypeError, sys.getdlopenflags, 42) - oldflags = sys.getdlopenflags() - self.assertRaises(TypeError, sys.setdlopenflags) - sys.setdlopenflags(oldflags+1) - self.assertEqual(sys.getdlopenflags(), oldflags+1) - sys.setdlopenflags(oldflags) + self.assertTrue(hasattr(sys, "getdlopenflags")) + self.assertRaises(TypeError, sys.getdlopenflags, 42) + oldflags = sys.getdlopenflags() + self.assertRaises(TypeError, sys.setdlopenflags) + sys.setdlopenflags(oldflags+1) + self.assertEqual(sys.getdlopenflags(), oldflags+1) + sys.setdlopenflags(oldflags) @test.support.refcount_test def test_refcount(self): diff --git a/Lib/test/test_warnings.py b/Lib/test/test_warnings.py index 464ff40d42..9f6d775c59 100644 --- a/Lib/test/test_warnings.py +++ b/Lib/test/test_warnings.py @@ -271,11 +271,10 @@ class WarnTests(BaseTest): finally: warning_tests.__file__ = filename + @unittest.skipUnless(hasattr(sys, 'argv'), 'test needs sys.argv') def test_missing_filename_main_with_argv(self): # If __file__ is not specified and the caller is __main__ and sys.argv # exists, then use sys.argv[0] as the file. - if not hasattr(sys, 'argv'): - return filename = warning_tests.__file__ module_name = warning_tests.__name__ try: diff --git a/Lib/test/test_wave.py b/Lib/test/test_wave.py index b4413c665b..cf069ae771 100644 --- a/Lib/test/test_wave.py +++ b/Lib/test/test_wave.py @@ -49,9 +49,6 @@ class WavePCM16Test(audiotests.AudioWriteTests, frames = audiotests.byteswap2(frames) -@unittest.skipIf(sys.byteorder == 'big', - '24-bit wave files are supported only on little-endian ' - 'platforms') class WavePCM24Test(audiotests.AudioWriteTests, audiotests.AudioTestsWithSourceFile, unittest.TestCase): diff --git a/Lib/test/test_zlib.py b/Lib/test/test_zlib.py index 2f6f84042f..1daa8f8a7e 100644 --- a/Lib/test/test_zlib.py +++ b/Lib/test/test_zlib.py @@ -7,6 +7,13 @@ from test.support import bigmemtest, _1G, _4G zlib = support.import_module('zlib') +requires_Compress_copy = unittest.skipUnless( + hasattr(zlib.compressobj(), "copy"), + 'requires Compress.copy()') +requires_Decompress_copy = unittest.skipUnless( + hasattr(zlib.decompressobj(), "copy"), + 'requires Decompress.copy()') + class VersionTestCase(unittest.TestCase): @@ -381,39 +388,39 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): "mode=%i, level=%i") % (sync, level)) del obj + @unittest.skipUnless(hasattr(zlib, 'Z_SYNC_FLUSH'), + 'requires zlib.Z_SYNC_FLUSH') def test_odd_flush(self): # Test for odd flushing bugs noted in 2.0, and hopefully fixed in 2.1 import random + # Testing on 17K of "random" data - if hasattr(zlib, 'Z_SYNC_FLUSH'): - # Testing on 17K of "random" data - - # Create compressor and decompressor objects - co = zlib.compressobj(zlib.Z_BEST_COMPRESSION) - dco = zlib.decompressobj() + # Create compressor and decompressor objects + co = zlib.compressobj(zlib.Z_BEST_COMPRESSION) + dco = zlib.decompressobj() - # Try 17K of data - # generate random data stream + # Try 17K of data + # generate random data stream + try: + # In 2.3 and later, WichmannHill is the RNG of the bug report + gen = random.WichmannHill() + except AttributeError: try: - # In 2.3 and later, WichmannHill is the RNG of the bug report - gen = random.WichmannHill() + # 2.2 called it Random + gen = random.Random() except AttributeError: - try: - # 2.2 called it Random - gen = random.Random() - except AttributeError: - # others might simply have a single RNG - gen = random - gen.seed(1) - data = genblock(1, 17 * 1024, generator=gen) - - # compress, sync-flush, and decompress - first = co.compress(data) - second = co.flush(zlib.Z_SYNC_FLUSH) - expanded = dco.decompress(first + second) - - # if decompressed data is different from the input data, choke. - self.assertEqual(expanded, data, "17K random source doesn't match") + # others might simply have a single RNG + gen = random + gen.seed(1) + data = genblock(1, 17 * 1024, generator=gen) + + # compress, sync-flush, and decompress + first = co.compress(data) + second = co.flush(zlib.Z_SYNC_FLUSH) + expanded = dco.decompress(first + second) + + # if decompressed data is different from the input data, choke. + self.assertEqual(expanded, data, "17K random source doesn't match") def test_empty_flush(self): # Test that calling .flush() on unused objects works. @@ -525,67 +532,69 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): data = zlib.compress(input2) self.assertEqual(dco.flush(), input1[1:]) - if hasattr(zlib.compressobj(), "copy"): - def test_compresscopy(self): - # Test copying a compression object - data0 = HAMLET_SCENE - data1 = bytes(str(HAMLET_SCENE, "ascii").swapcase(), "ascii") - c0 = zlib.compressobj(zlib.Z_BEST_COMPRESSION) - bufs0 = [] - bufs0.append(c0.compress(data0)) - - c1 = c0.copy() - bufs1 = bufs0[:] - - bufs0.append(c0.compress(data0)) - bufs0.append(c0.flush()) - s0 = b''.join(bufs0) - - bufs1.append(c1.compress(data1)) - bufs1.append(c1.flush()) - s1 = b''.join(bufs1) - - self.assertEqual(zlib.decompress(s0),data0+data0) - self.assertEqual(zlib.decompress(s1),data0+data1) - - def test_badcompresscopy(self): - # Test copying a compression object in an inconsistent state - c = zlib.compressobj() - c.compress(HAMLET_SCENE) - c.flush() - self.assertRaises(ValueError, c.copy) - - if hasattr(zlib.decompressobj(), "copy"): - def test_decompresscopy(self): - # Test copying a decompression object - data = HAMLET_SCENE - comp = zlib.compress(data) - # Test type of return value - self.assertIsInstance(comp, bytes) - - d0 = zlib.decompressobj() - bufs0 = [] - bufs0.append(d0.decompress(comp[:32])) - - d1 = d0.copy() - bufs1 = bufs0[:] - - bufs0.append(d0.decompress(comp[32:])) - s0 = b''.join(bufs0) - - bufs1.append(d1.decompress(comp[32:])) - s1 = b''.join(bufs1) - - self.assertEqual(s0,s1) - self.assertEqual(s0,data) - - def test_baddecompresscopy(self): - # Test copying a compression object in an inconsistent state - data = zlib.compress(HAMLET_SCENE) - d = zlib.decompressobj() - d.decompress(data) - d.flush() - self.assertRaises(ValueError, d.copy) + @requires_Compress_copy + def test_compresscopy(self): + # Test copying a compression object + data0 = HAMLET_SCENE + data1 = bytes(str(HAMLET_SCENE, "ascii").swapcase(), "ascii") + c0 = zlib.compressobj(zlib.Z_BEST_COMPRESSION) + bufs0 = [] + bufs0.append(c0.compress(data0)) + + c1 = c0.copy() + bufs1 = bufs0[:] + + bufs0.append(c0.compress(data0)) + bufs0.append(c0.flush()) + s0 = b''.join(bufs0) + + bufs1.append(c1.compress(data1)) + bufs1.append(c1.flush()) + s1 = b''.join(bufs1) + + self.assertEqual(zlib.decompress(s0),data0+data0) + self.assertEqual(zlib.decompress(s1),data0+data1) + + @requires_Compress_copy + def test_badcompresscopy(self): + # Test copying a compression object in an inconsistent state + c = zlib.compressobj() + c.compress(HAMLET_SCENE) + c.flush() + self.assertRaises(ValueError, c.copy) + + @requires_Decompress_copy + def test_decompresscopy(self): + # Test copying a decompression object + data = HAMLET_SCENE + comp = zlib.compress(data) + # Test type of return value + self.assertIsInstance(comp, bytes) + + d0 = zlib.decompressobj() + bufs0 = [] + bufs0.append(d0.decompress(comp[:32])) + + d1 = d0.copy() + bufs1 = bufs0[:] + + bufs0.append(d0.decompress(comp[32:])) + s0 = b''.join(bufs0) + + bufs1.append(d1.decompress(comp[32:])) + s1 = b''.join(bufs1) + + self.assertEqual(s0,s1) + self.assertEqual(s0,data) + + @requires_Decompress_copy + def test_baddecompresscopy(self): + # Test copying a compression object in an inconsistent state + data = zlib.compress(HAMLET_SCENE) + d = zlib.decompressobj() + d.decompress(data) + d.flush() + self.assertRaises(ValueError, d.copy) # Memory use of the following functions takes into account overallocation diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 2191ca7fc9..9cb228429d 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -2990,8 +2990,9 @@ class Text(Widget, XView, YView): def debug(self, boolean=None): """Turn on the internal consistency checks of the B-Tree inside the text widget according to BOOLEAN.""" - return self.tk.getboolean(self.tk.call( - self._w, 'debug', boolean)) + if boolean is None: + return self.tk.call(self._w, 'debug') + self.tk.call(self._w, 'debug', boolean) def delete(self, index1, index2=None): """Delete the characters between INDEX1 and INDEX2 (not included).""" self.tk.call(self._w, 'delete', index1, index2) @@ -3511,7 +3512,7 @@ class Spinbox(Widget, XView): bounding box may refer to a region outside the visible area of the window. """ - return self.tk.call(self._w, 'bbox', index) + return self._getints(self.tk.call(self._w, 'bbox', index)) or None def delete(self, first, last=None): """Delete one or more elements of the spinbox. diff --git a/Lib/tkinter/test/support.py b/Lib/tkinter/test/support.py index 6dd6d4a3fc..fcd9ffc1ca 100644 --- a/Lib/tkinter/test/support.py +++ b/Lib/tkinter/test/support.py @@ -77,3 +77,57 @@ def simulate_mouse_click(widget, x, y): widget.event_generate('<Motion>', x=x, y=y) widget.event_generate('<ButtonPress-1>', x=x, y=y) widget.event_generate('<ButtonRelease-1>', x=x, y=y) + + +import _tkinter +tcl_version = tuple(map(int, _tkinter.TCL_VERSION.split('.'))) + +def requires_tcl(*version): + return unittest.skipUnless(tcl_version >= version, + 'requires Tcl version >= ' + '.'.join(map(str, version))) + +_tk_patchlevel = None +def get_tk_patchlevel(): + global _tk_patchlevel + if _tk_patchlevel is None: + tcl = tkinter.Tcl() + patchlevel = [] + for x in tcl.call('info', 'patchlevel').split('.'): + try: + x = int(x, 10) + except ValueError: + x = -1 + patchlevel.append(x) + _tk_patchlevel = tuple(patchlevel) + return _tk_patchlevel + +units = { + 'c': 72 / 2.54, # centimeters + 'i': 72, # inches + 'm': 72 / 25.4, # millimeters + 'p': 1, # points +} + +def pixels_conv(value): + return float(value[:-1]) * units[value[-1:]] + +def tcl_obj_eq(actual, expected): + if actual == expected: + return True + if isinstance(actual, _tkinter.Tcl_Obj): + if isinstance(expected, str): + return str(actual) == expected + if isinstance(actual, tuple): + if isinstance(expected, tuple): + return (len(actual) == len(expected) and + all(tcl_obj_eq(act, exp) + for act, exp in zip(actual, expected))) + return False + +def widget_eq(actual, expected): + if actual == expected: + return True + if isinstance(actual, (str, tkinter.Widget)): + if isinstance(expected, (str, tkinter.Widget)): + return str(actual) == str(expected) + return False diff --git a/Lib/tkinter/test/test_tkinter/test_text.py b/Lib/tkinter/test/test_tkinter/test_text.py index a93c4ce25e..4c3fa04188 100644 --- a/Lib/tkinter/test/test_tkinter/test_text.py +++ b/Lib/tkinter/test/test_tkinter/test_text.py @@ -14,6 +14,17 @@ class TextTest(unittest.TestCase): def tearDown(self): self.text.destroy() + def test_debug(self): + text = self.text + olddebug = text.debug() + try: + text.debug(0) + self.assertEqual(text.debug(), 0) + text.debug(1) + self.assertEqual(text.debug(), 1) + finally: + text.debug(olddebug) + self.assertEqual(text.debug(), olddebug) def test_search(self): text = self.text diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py new file mode 100644 index 0000000000..ed4914fcad --- /dev/null +++ b/Lib/tkinter/test/test_tkinter/test_widgets.py @@ -0,0 +1,950 @@ +import unittest +import tkinter +import os +from test.support import requires + +from tkinter.test.support import (tcl_version, requires_tcl, + get_tk_patchlevel, widget_eq) +from tkinter.test.widget_tests import ( + add_standard_options, noconv, pixels_round, + AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests) + +requires('gui') + + +def float_round(x): + return float(round(x)) + + +class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): + _conv_pad_pixels = noconv + + def test_class(self): + widget = self.create() + self.assertEqual(widget['class'], + widget.__class__.__name__.title()) + self.checkInvalidParam(widget, 'class', 'Foo', + errmsg="can't modify -class option after widget is created") + widget2 = self.create(class_='Foo') + self.assertEqual(widget2['class'], 'Foo') + + def test_colormap(self): + widget = self.create() + self.assertEqual(widget['colormap'], '') + self.checkInvalidParam(widget, 'colormap', 'new', + errmsg="can't modify -colormap option after widget is created") + widget2 = self.create(colormap='new') + self.assertEqual(widget2['colormap'], 'new') + + def test_container(self): + widget = self.create() + self.assertEqual(widget['container'], 0 if self.wantobjects else '0') + self.checkInvalidParam(widget, 'container', 1, + errmsg="can't modify -container option after widget is created") + widget2 = self.create(container=True) + self.assertEqual(widget2['container'], 1 if self.wantobjects else '1') + + def test_visual(self): + widget = self.create() + self.assertEqual(widget['visual'], '') + self.checkInvalidParam(widget, 'visual', 'default', + errmsg="can't modify -visual option after widget is created") + widget2 = self.create(visual='default') + self.assertEqual(widget2['visual'], 'default') + + +@add_standard_options(StandardOptionsTests) +class ToplevelTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', + 'class', 'colormap', 'container', 'cursor', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'menu', 'padx', 'pady', 'relief', 'screen', + 'takefocus', 'use', 'visual', 'width', + ) + + def _create(self, **kwargs): + return tkinter.Toplevel(self.root, **kwargs) + + def test_menu(self): + widget = self.create() + menu = tkinter.Menu(self.root) + self.checkParam(widget, 'menu', menu, eq=widget_eq) + self.checkParam(widget, 'menu', '') + + def test_screen(self): + widget = self.create() + self.assertEqual(widget['screen'], '') + try: + display = os.environ['DISPLAY'] + except KeyError: + self.skipTest('No $DISPLAY set.') + self.checkInvalidParam(widget, 'screen', display, + errmsg="can't modify -screen option after widget is created") + widget2 = self.create(screen=display) + self.assertEqual(widget2['screen'], display) + + def test_use(self): + widget = self.create() + self.assertEqual(widget['use'], '') + parent = self.create(container=True) + wid = parent.winfo_id() + widget2 = self.create(use=wid) + self.assertEqual(int(widget2['use']), wid) + + +@add_standard_options(StandardOptionsTests) +class FrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', + 'class', 'colormap', 'container', 'cursor', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'relief', 'takefocus', 'visual', 'width', + ) + + def _create(self, **kwargs): + return tkinter.Frame(self.root, **kwargs) + + +@add_standard_options(StandardOptionsTests) +class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', + 'class', 'colormap', 'container', 'cursor', + 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'labelanchor', 'labelwidget', 'padx', 'pady', 'relief', + 'takefocus', 'text', 'visual', 'width', + ) + + def _create(self, **kwargs): + return tkinter.LabelFrame(self.root, **kwargs) + + def test_labelanchor(self): + widget = self.create() + self.checkEnumParam(widget, 'labelanchor', + 'e', 'en', 'es', 'n', 'ne', 'nw', + 's', 'se', 'sw', 'w', 'wn', 'ws') + self.checkInvalidParam(widget, 'labelanchor', 'center') + + def test_labelwidget(self): + widget = self.create() + label = tkinter.Label(self.root, text='Mupp', name='foo') + self.checkParam(widget, 'labelwidget', label, expected='.foo') + label.destroy() + + +class AbstractLabelTest(AbstractWidgetTest, IntegerSizeTests): + _conv_pixels = noconv + + def test_highlightthickness(self): + widget = self.create() + self.checkPixelsParam(widget, 'highlightthickness', + 0, 1.3, 2.6, 6, -2, '10p') + + +@add_standard_options(StandardOptionsTests) +class LabelTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', 'compound', 'cursor', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'justify', 'padx', 'pady', 'relief', 'state', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength', + ) + + def _create(self, **kwargs): + return tkinter.Label(self.root, **kwargs) + + +@add_standard_options(StandardOptionsTests) +class ButtonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'command', 'compound', 'cursor', 'default', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'justify', 'overrelief', 'padx', 'pady', 'relief', + 'repeatdelay', 'repeatinterval', + 'state', 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength') + + def _create(self, **kwargs): + return tkinter.Button(self.root, **kwargs) + + def test_default(self): + widget = self.create() + self.checkEnumParam(widget, 'default', 'active', 'disabled', 'normal') + + +@add_standard_options(StandardOptionsTests) +class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'command', 'compound', 'cursor', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'indicatoron', 'justify', + 'offrelief', 'offvalue', 'onvalue', 'overrelief', + 'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state', + 'takefocus', 'text', 'textvariable', + 'tristateimage', 'tristatevalue', + 'underline', 'variable', 'width', 'wraplength', + ) + + def _create(self, **kwargs): + return tkinter.Checkbutton(self.root, **kwargs) + + + def test_offvalue(self): + widget = self.create() + self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') + + def test_onvalue(self): + widget = self.create() + self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') + + +@add_standard_options(StandardOptionsTests) +class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'command', 'compound', 'cursor', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'indicatoron', 'justify', 'offrelief', 'overrelief', + 'padx', 'pady', 'relief', 'selectcolor', 'selectimage', 'state', + 'takefocus', 'text', 'textvariable', + 'tristateimage', 'tristatevalue', + 'underline', 'value', 'variable', 'width', 'wraplength', + ) + + def _create(self, **kwargs): + return tkinter.Radiobutton(self.root, **kwargs) + + def test_value(self): + widget = self.create() + self.checkParams(widget, 'value', 1, 2.3, '', 'any string') + + +@add_standard_options(StandardOptionsTests) +class MenubuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', + 'compound', 'cursor', 'direction', + 'disabledforeground', 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'indicatoron', 'justify', 'menu', + 'padx', 'pady', 'relief', 'state', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength', + ) + _conv_pixels = staticmethod(pixels_round) + + def _create(self, **kwargs): + return tkinter.Menubutton(self.root, **kwargs) + + def test_direction(self): + widget = self.create() + self.checkEnumParam(widget, 'direction', + 'above', 'below', 'flush', 'left', 'right') + + def test_height(self): + widget = self.create() + self.checkIntegerParam(widget, 'height', 100, -100, 0, conv=str) + + test_highlightthickness = StandardOptionsTests.test_highlightthickness + + def test_image(self): + widget = self.create() + image = tkinter.PhotoImage('image1') + self.checkParam(widget, 'image', image, conv=str) + errmsg = 'image "spam" doesn\'t exist' + with self.assertRaises(tkinter.TclError) as cm: + widget['image'] = 'spam' + if errmsg is not None: + self.assertEqual(str(cm.exception), errmsg) + with self.assertRaises(tkinter.TclError) as cm: + widget.configure({'image': 'spam'}) + if errmsg is not None: + self.assertEqual(str(cm.exception), errmsg) + + def test_menu(self): + widget = self.create() + menu = tkinter.Menu(widget, name='menu') + self.checkParam(widget, 'menu', menu, eq=widget_eq) + menu.destroy() + + def test_padx(self): + widget = self.create() + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, '12m') + self.checkParam(widget, 'padx', -2, expected=0) + + def test_pady(self): + widget = self.create() + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, '12m') + self.checkParam(widget, 'pady', -2, expected=0) + + def test_width(self): + widget = self.create() + self.checkIntegerParam(widget, 'width', 402, -402, 0, conv=str) + + +class OptionMenuTest(MenubuttonTest, unittest.TestCase): + + def _create(self, default='b', values=('a', 'b', 'c'), **kwargs): + return tkinter.OptionMenu(self.root, None, default, *values, **kwargs) + + +@add_standard_options(IntegerSizeTests, StandardOptionsTests) +class EntryTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', 'cursor', + 'disabledbackground', 'disabledforeground', + 'exportselection', 'font', 'foreground', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'invalidcommand', 'justify', 'readonlybackground', 'relief', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'show', 'state', 'takefocus', 'textvariable', + 'validate', 'validatecommand', 'width', 'xscrollcommand', + ) + + def _create(self, **kwargs): + return tkinter.Entry(self.root, **kwargs) + + def test_disabledbackground(self): + widget = self.create() + self.checkColorParam(widget, 'disabledbackground') + + def test_insertborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertborderwidth', 0, 1.3, -2) + self.checkParam(widget, 'insertborderwidth', 2, expected=1) + self.checkParam(widget, 'insertborderwidth', '10p', expected=1) + + def test_insertwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertwidth', 1.3, 3.6, '10p') + self.checkParam(widget, 'insertwidth', 0.1, expected=2) + self.checkParam(widget, 'insertwidth', -2, expected=2) + if pixels_round(0.9) <= 0: + self.checkParam(widget, 'insertwidth', 0.9, expected=2) + else: + self.checkParam(widget, 'insertwidth', 0.9, expected=1) + + def test_invalidcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'invalidcommand') + self.checkCommandParam(widget, 'invcmd') + + def test_readonlybackground(self): + widget = self.create() + self.checkColorParam(widget, 'readonlybackground') + + def test_show(self): + widget = self.create() + self.checkParam(widget, 'show', '*') + self.checkParam(widget, 'show', '') + self.checkParam(widget, 'show', ' ') + + def test_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', + 'disabled', 'normal', 'readonly') + + def test_validate(self): + widget = self.create() + self.checkEnumParam(widget, 'validate', + 'all', 'key', 'focus', 'focusin', 'focusout', 'none') + + def test_validatecommand(self): + widget = self.create() + self.checkCommandParam(widget, 'validatecommand') + self.checkCommandParam(widget, 'vcmd') + + +@add_standard_options(StandardOptionsTests) +class SpinboxTest(EntryTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'background', 'borderwidth', + 'buttonbackground', 'buttoncursor', 'buttondownrelief', 'buttonuprelief', + 'command', 'cursor', 'disabledbackground', 'disabledforeground', + 'exportselection', 'font', 'foreground', 'format', 'from', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'increment', + 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'invalidcommand', 'justify', 'relief', 'readonlybackground', + 'repeatdelay', 'repeatinterval', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'state', 'takefocus', 'textvariable', 'to', + 'validate', 'validatecommand', 'values', + 'width', 'wrap', 'xscrollcommand', + ) + + def _create(self, **kwargs): + return tkinter.Spinbox(self.root, **kwargs) + + test_show = None + + def test_buttonbackground(self): + widget = self.create() + self.checkColorParam(widget, 'buttonbackground') + + def test_buttoncursor(self): + widget = self.create() + self.checkCursorParam(widget, 'buttoncursor') + + def test_buttondownrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'buttondownrelief') + + def test_buttonuprelief(self): + widget = self.create() + self.checkReliefParam(widget, 'buttonuprelief') + + def test_format(self): + widget = self.create() + self.checkParam(widget, 'format', '%2f') + self.checkParam(widget, 'format', '%2.2f') + self.checkParam(widget, 'format', '%.2f') + self.checkParam(widget, 'format', '%2.f') + self.checkInvalidParam(widget, 'format', '%2e-1f') + self.checkInvalidParam(widget, 'format', '2.2') + self.checkInvalidParam(widget, 'format', '%2.-2f') + self.checkParam(widget, 'format', '%-2.02f') + self.checkParam(widget, 'format', '% 2.02f') + self.checkParam(widget, 'format', '% -2.200f') + self.checkParam(widget, 'format', '%09.200f') + self.checkInvalidParam(widget, 'format', '%d') + + def test_from(self): + widget = self.create() + self.checkParam(widget, 'to', 100.0) + self.checkFloatParam(widget, 'from', -10, 10.2, 11.7) + self.checkInvalidParam(widget, 'from', 200, + errmsg='-to value must be greater than -from value') + + def test_increment(self): + widget = self.create() + self.checkFloatParam(widget, 'increment', -1, 1, 10.2, 12.8, 0) + + def test_to(self): + widget = self.create() + self.checkParam(widget, 'from', -100.0) + self.checkFloatParam(widget, 'to', -10, 10.2, 11.7) + self.checkInvalidParam(widget, 'to', -200, + errmsg='-to value must be greater than -from value') + + def test_values(self): + # XXX + widget = self.create() + self.assertEqual(widget['values'], '') + self.checkParam(widget, 'values', 'mon tue wed thur') + self.checkParam(widget, 'values', ('mon', 'tue', 'wed', 'thur'), + expected='mon tue wed thur') + self.checkParam(widget, 'values', (42, 3.14, '', 'any string'), + expected='42 3.14 {} {any string}') + self.checkParam(widget, 'values', '') + + def test_wrap(self): + widget = self.create() + self.checkBooleanParam(widget, 'wrap') + + def test_bbox(self): + widget = self.create() + bbox = widget.bbox(0) + self.assertEqual(len(bbox), 4) + for item in bbox: + self.assertIsInstance(item, int) + + self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') + self.assertRaises(tkinter.TclError, widget.bbox, None) + self.assertRaises(TypeError, widget.bbox) + self.assertRaises(TypeError, widget.bbox, 0, 1) + + +@add_standard_options(StandardOptionsTests) +class TextTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'autoseparators', 'background', 'blockcursor', 'borderwidth', + 'cursor', 'endline', 'exportselection', + 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'inactiveselectbackground', 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertunfocussed', 'insertwidth', + 'maxundo', 'padx', 'pady', 'relief', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'setgrid', 'spacing1', 'spacing2', 'spacing3', 'startline', 'state', + 'tabs', 'tabstyle', 'takefocus', 'undo', 'width', 'wrap', + 'xscrollcommand', 'yscrollcommand', + ) + if tcl_version < (8, 5): + wantobjects = False + + def _create(self, **kwargs): + return tkinter.Text(self.root, **kwargs) + + def test_autoseparators(self): + widget = self.create() + self.checkBooleanParam(widget, 'autoseparators') + + @requires_tcl(8, 5) + def test_blockcursor(self): + widget = self.create() + self.checkBooleanParam(widget, 'blockcursor') + + @requires_tcl(8, 5) + def test_endline(self): + widget = self.create() + text = '\n'.join('Line %d' for i in range(100)) + widget.insert('end', text) + self.checkParam(widget, 'endline', 200, expected='') + self.checkParam(widget, 'endline', -10, expected='') + self.checkInvalidParam(widget, 'endline', 'spam', + errmsg='expected integer but got "spam"') + self.checkParam(widget, 'endline', 50) + self.checkParam(widget, 'startline', 15) + self.checkInvalidParam(widget, 'endline', 10, + errmsg='-startline must be less than or equal to -endline') + + def test_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, '3c') + self.checkParam(widget, 'height', -100, expected=1) + self.checkParam(widget, 'height', 0, expected=1) + + def test_maxundo(self): + widget = self.create() + self.checkIntegerParam(widget, 'maxundo', 0, 5, -1) + + @requires_tcl(8, 5) + def test_inactiveselectbackground(self): + widget = self.create() + self.checkColorParam(widget, 'inactiveselectbackground') + + @requires_tcl(8, 6) + def test_insertunfocussed(self): + widget = self.create() + self.checkEnumParam(widget, 'insertunfocussed', + 'hollow', 'none', 'solid') + + def test_selectborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'selectborderwidth', + 1.3, 2.6, -2, '10p', conv=noconv, + keep_orig=tcl_version >= (8, 5)) + + def test_spacing1(self): + widget = self.create() + self.checkPixelsParam(widget, 'spacing1', 20, 21.4, 22.6, '0.5c') + self.checkParam(widget, 'spacing1', -5, expected=0) + + def test_spacing2(self): + widget = self.create() + self.checkPixelsParam(widget, 'spacing2', 5, 6.4, 7.6, '0.1c') + self.checkParam(widget, 'spacing2', -1, expected=0) + + def test_spacing3(self): + widget = self.create() + self.checkPixelsParam(widget, 'spacing3', 20, 21.4, 22.6, '0.5c') + self.checkParam(widget, 'spacing3', -10, expected=0) + + @requires_tcl(8, 5) + def test_startline(self): + widget = self.create() + text = '\n'.join('Line %d' for i in range(100)) + widget.insert('end', text) + self.checkParam(widget, 'startline', 200, expected='') + self.checkParam(widget, 'startline', -10, expected='') + self.checkInvalidParam(widget, 'startline', 'spam', + errmsg='expected integer but got "spam"') + self.checkParam(widget, 'startline', 10) + self.checkParam(widget, 'endline', 50) + self.checkInvalidParam(widget, 'startline', 70, + errmsg='-startline must be less than or equal to -endline') + + def test_state(self): + widget = self.create() + if tcl_version < (8, 5): + self.checkParams(widget, 'state', 'disabled', 'normal') + else: + self.checkEnumParam(widget, 'state', 'disabled', 'normal') + + def test_tabs(self): + widget = self.create() + if get_tk_patchlevel() < (8, 5, 11): + self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i'), + expected=('10.2', '20.7', '1i', '2i')) + else: + self.checkParam(widget, 'tabs', (10.2, 20.7, '1i', '2i')) + self.checkParam(widget, 'tabs', '10.2 20.7 1i 2i', + expected=('10.2', '20.7', '1i', '2i')) + self.checkParam(widget, 'tabs', '2c left 4c 6c center', + expected=('2c', 'left', '4c', '6c', 'center')) + self.checkInvalidParam(widget, 'tabs', 'spam', + errmsg='bad screen distance "spam"', + keep_orig=tcl_version >= (8, 5)) + + @requires_tcl(8, 5) + def test_tabstyle(self): + widget = self.create() + self.checkEnumParam(widget, 'tabstyle', 'tabular', 'wordprocessor') + + def test_undo(self): + widget = self.create() + self.checkBooleanParam(widget, 'undo') + + def test_width(self): + widget = self.create() + self.checkIntegerParam(widget, 'width', 402) + self.checkParam(widget, 'width', -402, expected=1) + self.checkParam(widget, 'width', 0, expected=1) + + def test_wrap(self): + widget = self.create() + if tcl_version < (8, 5): + self.checkParams(widget, 'wrap', 'char', 'none', 'word') + else: + self.checkEnumParam(widget, 'wrap', 'char', 'none', 'word') + + def test_bbox(self): + widget = self.create() + bbox = widget.bbox('1.1') + self.assertEqual(len(bbox), 4) + for item in bbox: + self.assertIsInstance(item, int) + + self.assertIsNone(widget.bbox('end')) + self.assertRaises(tkinter.TclError, widget.bbox, 'noindex') + self.assertRaises(tkinter.TclError, widget.bbox, None) + self.assertRaises(tkinter.TclError, widget.bbox) + self.assertRaises(tkinter.TclError, widget.bbox, '1.1', 'end') + + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class CanvasTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', + 'closeenough', 'confine', 'cursor', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'relief', 'scrollregion', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'state', 'takefocus', + 'xscrollcommand', 'xscrollincrement', + 'yscrollcommand', 'yscrollincrement', 'width', + ) + + _conv_pixels = round + wantobjects = False + + def _create(self, **kwargs): + return tkinter.Canvas(self.root, **kwargs) + + def test_closeenough(self): + widget = self.create() + self.checkFloatParam(widget, 'closeenough', 24, 2.4, 3.6, -3, + conv=float) + + def test_confine(self): + widget = self.create() + self.checkBooleanParam(widget, 'confine') + + def test_scrollregion(self): + widget = self.create() + self.checkParam(widget, 'scrollregion', '0 0 200 150') + self.checkParam(widget, 'scrollregion', (0, 0, 200, 150), + expected='0 0 200 150') + self.checkParam(widget, 'scrollregion', '') + self.checkInvalidParam(widget, 'scrollregion', 'spam', + errmsg='bad scrollRegion "spam"') + self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 'spam')) + self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200)) + self.checkInvalidParam(widget, 'scrollregion', (0, 0, 200, 150, 0)) + + def test_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', 'disabled', 'normal', + errmsg='bad state value "{}": must be normal or disabled') + + def test_xscrollincrement(self): + widget = self.create() + self.checkPixelsParam(widget, 'xscrollincrement', + 40, 0, 41.2, 43.6, -40, '0.5i') + + def test_yscrollincrement(self): + widget = self.create() + self.checkPixelsParam(widget, 'yscrollincrement', + 10, 0, 11.2, 13.6, -10, '0.1i') + + +@add_standard_options(IntegerSizeTests, StandardOptionsTests) +class ListboxTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activestyle', 'background', 'borderwidth', 'cursor', + 'disabledforeground', 'exportselection', + 'font', 'foreground', 'height', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'listvariable', 'relief', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'selectmode', 'setgrid', 'state', + 'takefocus', 'width', 'xscrollcommand', 'yscrollcommand', + ) + + def _create(self, **kwargs): + return tkinter.Listbox(self.root, **kwargs) + + def test_activestyle(self): + widget = self.create() + self.checkEnumParam(widget, 'activestyle', + 'dotbox', 'none', 'underline') + + def test_listvariable(self): + widget = self.create() + var = tkinter.DoubleVar() + self.checkVariableParam(widget, 'listvariable', var) + + def test_selectmode(self): + widget = self.create() + self.checkParam(widget, 'selectmode', 'single') + self.checkParam(widget, 'selectmode', 'browse') + self.checkParam(widget, 'selectmode', 'multiple') + self.checkParam(widget, 'selectmode', 'extended') + + def test_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', 'disabled', 'normal') + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class ScaleTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'background', 'bigincrement', 'borderwidth', + 'command', 'cursor', 'digits', 'font', 'foreground', 'from', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'label', 'length', 'orient', 'relief', + 'repeatdelay', 'repeatinterval', + 'resolution', 'showvalue', 'sliderlength', 'sliderrelief', 'state', + 'takefocus', 'tickinterval', 'to', 'troughcolor', 'variable', 'width', + ) + default_orient = 'vertical' + + def _create(self, **kwargs): + return tkinter.Scale(self.root, **kwargs) + + def test_bigincrement(self): + widget = self.create() + self.checkFloatParam(widget, 'bigincrement', 12.4, 23.6, -5) + + def test_digits(self): + widget = self.create() + self.checkIntegerParam(widget, 'digits', 5, 0) + + def test_from(self): + widget = self.create() + self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=float_round) + + def test_label(self): + widget = self.create() + self.checkParam(widget, 'label', 'any string') + self.checkParam(widget, 'label', '') + + def test_length(self): + widget = self.create() + self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') + + def test_resolution(self): + widget = self.create() + self.checkFloatParam(widget, 'resolution', 4.2, 0, 6.7, -2) + + def test_showvalue(self): + widget = self.create() + self.checkBooleanParam(widget, 'showvalue') + + def test_sliderlength(self): + widget = self.create() + self.checkPixelsParam(widget, 'sliderlength', + 10, 11.2, 15.6, -3, '3m') + + def test_sliderrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'sliderrelief') + + def test_tickinterval(self): + widget = self.create() + self.checkFloatParam(widget, 'tickinterval', 1, 4.3, 7.6, 0, + conv=float_round) + self.checkParam(widget, 'tickinterval', -2, expected=2, + conv=float_round) + + def test_to(self): + widget = self.create() + self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, + conv=float_round) + + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activerelief', + 'background', 'borderwidth', + 'command', 'cursor', 'elementborderwidth', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'jump', 'orient', 'relief', + 'repeatdelay', 'repeatinterval', + 'takefocus', 'troughcolor', 'width', + ) + _conv_pixels = round + wantobjects = False + default_orient = 'vertical' + + def _create(self, **kwargs): + return tkinter.Scrollbar(self.root, **kwargs) + + def test_activerelief(self): + widget = self.create() + self.checkReliefParam(widget, 'activerelief') + + def test_elementborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'elementborderwidth', 4.3, 5.6, -2, '1m') + + def test_orient(self): + widget = self.create() + self.checkEnumParam(widget, 'orient', 'vertical', 'horizontal', + errmsg='bad orientation "{}": must be vertical or horizontal') + + +@add_standard_options(StandardOptionsTests) +class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'borderwidth', 'cursor', + 'handlepad', 'handlesize', 'height', + 'opaqueresize', 'orient', 'relief', + 'sashcursor', 'sashpad', 'sashrelief', 'sashwidth', + 'showhandle', 'width', + ) + default_orient = 'horizontal' + + def _create(self, **kwargs): + return tkinter.PanedWindow(self.root, **kwargs) + + def test_handlepad(self): + widget = self.create() + self.checkPixelsParam(widget, 'handlepad', 5, 6.4, 7.6, -3, '1m') + + def test_handlesize(self): + widget = self.create() + self.checkPixelsParam(widget, 'handlesize', 8, 9.4, 10.6, -3, '2m', + conv=noconv) + + def test_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i', + conv=noconv) + + def test_opaqueresize(self): + widget = self.create() + self.checkBooleanParam(widget, 'opaqueresize') + + def test_sashcursor(self): + widget = self.create() + self.checkCursorParam(widget, 'sashcursor') + + def test_sashpad(self): + widget = self.create() + self.checkPixelsParam(widget, 'sashpad', 8, 1.3, 2.6, -2, '2m') + + def test_sashrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'sashrelief') + + def test_sashwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'sashwidth', 10, 11.1, 15.6, -3, '1m', + conv=noconv) + + def test_showhandle(self): + widget = self.create() + self.checkBooleanParam(widget, 'showhandle') + + def test_width(self): + widget = self.create() + self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i', + conv=noconv) + + +@add_standard_options(StandardOptionsTests) +class MenuTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'activebackground', 'activeborderwidth', 'activeforeground', + 'background', 'borderwidth', 'cursor', + 'disabledforeground', 'font', 'foreground', + 'postcommand', 'relief', 'selectcolor', 'takefocus', + 'tearoff', 'tearoffcommand', 'title', 'type', + ) + _conv_pixels = noconv + + def _create(self, **kwargs): + return tkinter.Menu(self.root, **kwargs) + + def test_postcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'postcommand') + + def test_tearoff(self): + widget = self.create() + self.checkBooleanParam(widget, 'tearoff') + + def test_tearoffcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'tearoffcommand') + + def test_title(self): + widget = self.create() + self.checkParam(widget, 'title', 'any string') + + def test_type(self): + widget = self.create() + self.checkEnumParam(widget, 'type', + 'normal', 'tearoff', 'menubar') + + +@add_standard_options(PixelSizeTests, StandardOptionsTests) +class MessageTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'anchor', 'aspect', 'background', 'borderwidth', + 'cursor', 'font', 'foreground', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'justify', 'padx', 'pady', 'relief', + 'takefocus', 'text', 'textvariable', 'width', + ) + _conv_pad_pixels = noconv + + def _create(self, **kwargs): + return tkinter.Message(self.root, **kwargs) + + def test_aspect(self): + widget = self.create() + self.checkIntegerParam(widget, 'aspect', 250, 0, -300) + + +tests_gui = ( + ButtonTest, CanvasTest, CheckbuttonTest, EntryTest, + FrameTest, LabelFrameTest,LabelTest, ListboxTest, + MenubuttonTest, MenuTest, MessageTest, OptionMenuTest, + PanedWindowTest, RadiobuttonTest, ScaleTest, ScrollbarTest, + SpinboxTest, TextTest, ToplevelTest, +) + +if __name__ == '__main__': + unittest.main() diff --git a/Lib/tkinter/test/test_ttk/test_widgets.py b/Lib/tkinter/test/test_ttk/test_widgets.py index 890787b34a..55dc2f080b 100644 --- a/Lib/tkinter/test/test_ttk/test_widgets.py +++ b/Lib/tkinter/test/test_ttk/test_widgets.py @@ -1,15 +1,57 @@ import unittest import tkinter -import os from tkinter import ttk -from test.support import requires, run_unittest +from test.support import requires import sys import tkinter.test.support as support -from tkinter.test.test_ttk.test_functions import MockTclObj, MockStateSpec +from tkinter.test.test_ttk.test_functions import MockTclObj +from tkinter.test.support import tcl_version +from tkinter.test.widget_tests import (add_standard_options, noconv, + AbstractWidgetTest, StandardOptionsTests, IntegerSizeTests, PixelSizeTests) requires('gui') + +class StandardTtkOptionsTests(StandardOptionsTests): + + def test_class(self): + widget = self.create() + self.assertEqual(widget['class'], '') + errmsg='attempt to change read-only option' + if tcl_version < (8, 6): + errmsg='Attempt to change read-only option' + self.checkInvalidParam(widget, 'class', 'Foo', errmsg=errmsg) + widget2 = self.create(class_='Foo') + self.assertEqual(widget2['class'], 'Foo') + + def test_padding(self): + widget = self.create() + self.checkParam(widget, 'padding', 0, expected=('0',)) + self.checkParam(widget, 'padding', 5, expected=('5',)) + self.checkParam(widget, 'padding', (5, 6), expected=('5', '6')) + self.checkParam(widget, 'padding', (5, 6, 7), + expected=('5', '6', '7')) + self.checkParam(widget, 'padding', (5, 6, 7, 8), + expected=('5', '6', '7', '8')) + self.checkParam(widget, 'padding', ('5p', '6p', '7p', '8p')) + self.checkParam(widget, 'padding', (), expected='') + + def test_style(self): + widget = self.create() + self.assertEqual(widget['style'], '') + errmsg = 'Layout Foo not found' + if hasattr(self, 'default_orient'): + errmsg = ('Layout %s.Foo not found' % + getattr(self, 'default_orient').title()) + self.checkInvalidParam(widget, 'style', 'Foo', + errmsg=errmsg) + widget2 = self.create(class_='Foo') + self.assertEqual(widget2['class'], 'Foo') + # XXX + pass + + class WidgetTest(unittest.TestCase): """Tests methods available in every ttk widget.""" @@ -73,7 +115,112 @@ class WidgetTest(unittest.TestCase): self.assertEqual(self.widget.state(), ('active', )) -class ButtonTest(unittest.TestCase): +class AbstractToplevelTest(AbstractWidgetTest, PixelSizeTests): + _conv_pixels = noconv + + +@add_standard_options(StandardTtkOptionsTests) +class FrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'borderwidth', 'class', 'cursor', 'height', + 'padding', 'relief', 'style', 'takefocus', + 'width', + ) + + def _create(self, **kwargs): + return ttk.Frame(self.root, **kwargs) + + +@add_standard_options(StandardTtkOptionsTests) +class LabelFrameTest(AbstractToplevelTest, unittest.TestCase): + OPTIONS = ( + 'borderwidth', 'class', 'cursor', 'height', + 'labelanchor', 'labelwidget', + 'padding', 'relief', 'style', 'takefocus', + 'text', 'underline', 'width', + ) + + def _create(self, **kwargs): + return ttk.LabelFrame(self.root, **kwargs) + + def test_labelanchor(self): + widget = self.create() + self.checkEnumParam(widget, 'labelanchor', + 'e', 'en', 'es', 'n', 'ne', 'nw', 's', 'se', 'sw', 'w', 'wn', 'ws', + errmsg='Bad label anchor specification {}') + self.checkInvalidParam(widget, 'labelanchor', 'center') + + def test_labelwidget(self): + widget = self.create() + label = ttk.Label(self.root, text='Mupp', name='foo') + self.checkParam(widget, 'labelwidget', label, expected='.foo') + label.destroy() + + +class AbstractLabelTest(AbstractWidgetTest): + + def checkImageParam(self, widget, name): + image = tkinter.PhotoImage('image1') + image2 = tkinter.PhotoImage('image2') + self.checkParam(widget, name, image, expected=('image1',)) + self.checkParam(widget, name, 'image1', expected=('image1',)) + self.checkParam(widget, name, (image,), expected=('image1',)) + self.checkParam(widget, name, (image, 'active', image2), + expected=('image1', 'active', 'image2')) + self.checkParam(widget, name, 'image1 active image2', + expected=('image1', 'active', 'image2')) + self.checkInvalidParam(widget, name, 'spam', + errmsg='image "spam" doesn\'t exist') + + def test_compound(self): + widget = self.create() + self.checkEnumParam(widget, 'compound', + 'none', 'text', 'image', 'center', + 'top', 'bottom', 'left', 'right') + + def test_state(self): + widget = self.create() + self.checkParams(widget, 'state', 'active', 'disabled', 'normal') + + def test_width(self): + widget = self.create() + self.checkParams(widget, 'width', 402, -402, 0) + + +@add_standard_options(StandardTtkOptionsTests) +class LabelTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'anchor', 'background', + 'class', 'compound', 'cursor', 'font', 'foreground', + 'image', 'justify', 'padding', 'relief', 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', 'wraplength', + ) + _conv_pixels = noconv + + def _create(self, **kwargs): + return ttk.Label(self.root, **kwargs) + + def test_font(self): + widget = self.create() + self.checkParam(widget, 'font', + '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') + + +@add_standard_options(StandardTtkOptionsTests) +class ButtonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'compound', 'cursor', 'default', + 'image', 'state', 'style', 'takefocus', 'text', 'textvariable', + 'underline', 'width', + ) + + def _create(self, **kwargs): + return ttk.Button(self.root, **kwargs) + + def test_default(self): + widget = self.create() + self.checkEnumParam(widget, 'default', 'normal', 'active', 'disabled') def test_invoke(self): success = [] @@ -82,7 +229,27 @@ class ButtonTest(unittest.TestCase): self.assertTrue(success) -class CheckbuttonTest(unittest.TestCase): +@add_standard_options(StandardTtkOptionsTests) +class CheckbuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'compound', 'cursor', + 'image', + 'offvalue', 'onvalue', + 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'variable', 'width', + ) + + def _create(self, **kwargs): + return ttk.Checkbutton(self.root, **kwargs) + + def test_offvalue(self): + widget = self.create() + self.checkParams(widget, 'offvalue', 1, 2.3, '', 'any string') + + def test_onvalue(self): + widget = self.create() + self.checkParams(widget, 'onvalue', 1, 2.3, '', 'any string') def test_invoke(self): success = [] @@ -105,21 +272,40 @@ class CheckbuttonTest(unittest.TestCase): cbtn['command'] = '' res = cbtn.invoke() - self.assertEqual(str(res), '') + self.assertFalse(str(res)) self.assertFalse(len(success) > 1) self.assertEqual(cbtn['offvalue'], cbtn.tk.globalgetvar(cbtn['variable'])) -class ComboboxTest(unittest.TestCase): +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class ComboboxTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'exportselection', 'height', + 'justify', 'postcommand', 'state', 'style', + 'takefocus', 'textvariable', 'values', 'width', + ) def setUp(self): + super().setUp() support.root_deiconify() - self.combo = ttk.Combobox() + self.combo = self.create() def tearDown(self): self.combo.destroy() support.root_withdraw() + super().tearDown() + + def _create(self, **kwargs): + return ttk.Combobox(self.root, **kwargs) + + def test_height(self): + widget = self.create() + self.checkParams(widget, 'height', 100, 101.2, 102.6, -100, 0, '1i') + + def test_state(self): + widget = self.create() + self.checkParams(widget, 'state', 'active', 'disabled', 'normal') def _show_drop_down_listbox(self): width = self.combo.winfo_width() @@ -167,8 +353,16 @@ class ComboboxTest(unittest.TestCase): self.assertEqual(self.combo.get(), getval) self.assertEqual(self.combo.current(), currval) + self.assertEqual(self.combo['values'], + () if tcl_version < (8, 5) else '') check_get_current('', -1) + self.checkParam(self.combo, 'values', 'mon tue wed thur', + expected=('mon', 'tue', 'wed', 'thur')) + self.checkParam(self.combo, 'values', ('mon', 'tue', 'wed', 'thur')) + self.checkParam(self.combo, 'values', (42, 3.14, '', 'any string')) + self.checkParam(self.combo, 'values', '', expected=()) + self.combo['values'] = ['a', 1, 'c'] self.combo.set('c') @@ -209,15 +403,52 @@ class ComboboxTest(unittest.TestCase): combo2.destroy() -class EntryTest(unittest.TestCase): +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class EntryTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'background', 'class', 'cursor', + 'exportselection', 'font', + 'invalidcommand', 'justify', + 'show', 'state', 'style', 'takefocus', 'textvariable', + 'validate', 'validatecommand', 'width', 'xscrollcommand', + ) def setUp(self): + super().setUp() support.root_deiconify() - self.entry = ttk.Entry() + self.entry = self.create() def tearDown(self): self.entry.destroy() support.root_withdraw() + super().tearDown() + + def _create(self, **kwargs): + return ttk.Entry(self.root, **kwargs) + + def test_invalidcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'invalidcommand') + + def test_show(self): + widget = self.create() + self.checkParam(widget, 'show', '*') + self.checkParam(widget, 'show', '') + self.checkParam(widget, 'show', ' ') + + def test_state(self): + widget = self.create() + self.checkParams(widget, 'state', + 'disabled', 'normal', 'readonly') + + def test_validate(self): + widget = self.create() + self.checkEnumParam(widget, 'validate', + 'all', 'key', 'focus', 'focusin', 'focusout', 'none') + + def test_validatecommand(self): + widget = self.create() + self.checkCommandParam(widget, 'validatecommand') def test_bbox(self): @@ -313,16 +544,36 @@ class EntryTest(unittest.TestCase): self.assertEqual(self.entry.state(), ()) -class PanedwindowTest(unittest.TestCase): +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class PanedWindowTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'height', + 'orient', 'style', 'takefocus', 'width', + ) def setUp(self): + super().setUp() support.root_deiconify() - self.paned = ttk.Panedwindow() + self.paned = self.create() def tearDown(self): self.paned.destroy() support.root_withdraw() - + super().tearDown() + + def _create(self, **kwargs): + return ttk.PanedWindow(self.root, **kwargs) + + def test_orient(self): + widget = self.create() + self.assertEqual(str(widget['orient']), 'vertical') + errmsg='attempt to change read-only option' + if tcl_version < (8, 6): + errmsg='Attempt to change read-only option' + self.checkInvalidParam(widget, 'orient', 'horizontal', + errmsg=errmsg) + widget2 = self.create(orient='horizontal') + self.assertEqual(str(widget2['orient']), 'horizontal') def test_add(self): # attempt to add a child that is not a direct child of the paned window @@ -432,7 +683,22 @@ class PanedwindowTest(unittest.TestCase): self.assertTrue(isinstance(self.paned.sashpos(0), int)) -class RadiobuttonTest(unittest.TestCase): +@add_standard_options(StandardTtkOptionsTests) +class RadiobuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'compound', 'cursor', + 'image', + 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'value', 'variable', 'width', + ) + + def _create(self, **kwargs): + return ttk.Radiobutton(self.root, **kwargs) + + def test_value(self): + widget = self.create() + self.checkParams(widget, 'value', 1, 2.3, '', 'any string') def test_invoke(self): success = [] @@ -462,19 +728,68 @@ class RadiobuttonTest(unittest.TestCase): self.assertEqual(str(cbtn['variable']), str(cbtn2['variable'])) +class MenubuttonTest(AbstractLabelTest, unittest.TestCase): + OPTIONS = ( + 'class', 'compound', 'cursor', 'direction', + 'image', 'menu', 'state', 'style', + 'takefocus', 'text', 'textvariable', + 'underline', 'width', + ) + + def _create(self, **kwargs): + return ttk.Menubutton(self.root, **kwargs) + + def test_direction(self): + widget = self.create() + self.checkEnumParam(widget, 'direction', + 'above', 'below', 'left', 'right', 'flush') + + def test_menu(self): + widget = self.create() + menu = tkinter.Menu(widget, name='menu') + self.checkParam(widget, 'menu', menu, conv=str) + menu.destroy() + -class ScaleTest(unittest.TestCase): +@add_standard_options(StandardTtkOptionsTests) +class ScaleTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'cursor', 'from', 'length', + 'orient', 'style', 'takefocus', 'to', 'value', 'variable', + ) + _conv_pixels = noconv + default_orient = 'horizontal' def setUp(self): + super().setUp() support.root_deiconify() - self.scale = ttk.Scale() + self.scale = self.create() self.scale.pack() self.scale.update() def tearDown(self): self.scale.destroy() support.root_withdraw() + super().tearDown() + def _create(self, **kwargs): + return ttk.Scale(self.root, **kwargs) + + def test_from(self): + widget = self.create() + self.checkFloatParam(widget, 'from', 100, 14.9, 15.1, conv=False) + + def test_length(self): + widget = self.create() + self.checkPixelsParam(widget, 'length', 130, 131.2, 135.6, '5i') + + def test_to(self): + widget = self.create() + self.checkFloatParam(widget, 'to', 300, 14.9, 15.1, -10, conv=False) + + def test_value(self): + widget = self.create() + self.checkFloatParam(widget, 'value', 300, 14.9, 15.1, -10, conv=False) def test_custom_event(self): failure = [1, 1, 1] # will need to be empty @@ -539,11 +854,64 @@ class ScaleTest(unittest.TestCase): self.assertRaises(tkinter.TclError, self.scale.set, None) -class NotebookTest(unittest.TestCase): +@add_standard_options(StandardTtkOptionsTests) +class ProgressbarTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'orient', 'length', + 'mode', 'maximum', 'phase', + 'style', 'takefocus', 'value', 'variable', + ) + _conv_pixels = noconv + default_orient = 'horizontal' + + def _create(self, **kwargs): + return ttk.Progressbar(self.root, **kwargs) + + def test_length(self): + widget = self.create() + self.checkPixelsParam(widget, 'length', 100.1, 56.7, '2i') + + def test_maximum(self): + widget = self.create() + self.checkFloatParam(widget, 'maximum', 150.2, 77.7, 0, -10, conv=False) + + def test_mode(self): + widget = self.create() + self.checkEnumParam(widget, 'mode', 'determinate', 'indeterminate') + + def test_phase(self): + # XXX + pass + + def test_value(self): + widget = self.create() + self.checkFloatParam(widget, 'value', 150.2, 77.7, 0, -10, + conv=False) + + +@unittest.skipIf(sys.platform == 'darwin', + 'ttk.Scrollbar is special on MacOSX') +@add_standard_options(StandardTtkOptionsTests) +class ScrollbarTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'command', 'cursor', 'orient', 'style', 'takefocus', + ) + default_orient = 'vertical' + + def _create(self, **kwargs): + return ttk.Scrollbar(self.root, **kwargs) + + +@add_standard_options(IntegerSizeTests, StandardTtkOptionsTests) +class NotebookTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'height', 'padding', 'style', 'takefocus', + ) def setUp(self): + super().setUp() support.root_deiconify() - self.nb = ttk.Notebook(padding=0) + self.nb = self.create(padding=0) self.child1 = ttk.Label() self.child2 = ttk.Label() self.nb.add(self.child1, text='a') @@ -554,7 +922,10 @@ class NotebookTest(unittest.TestCase): self.child2.destroy() self.nb.destroy() support.root_withdraw() + super().tearDown() + def _create(self, **kwargs): + return ttk.Notebook(self.root, **kwargs) def test_tab_identifiers(self): self.nb.forget(0) @@ -746,16 +1117,68 @@ class NotebookTest(unittest.TestCase): self.assertEqual(self.nb.select(), str(self.child1)) -class TreeviewTest(unittest.TestCase): +@add_standard_options(StandardTtkOptionsTests) +class TreeviewTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'columns', 'cursor', 'displaycolumns', + 'height', 'padding', 'selectmode', 'show', + 'style', 'takefocus', 'xscrollcommand', 'yscrollcommand', + ) def setUp(self): + super().setUp() support.root_deiconify() - self.tv = ttk.Treeview(padding=0) + self.tv = self.create(padding=0) def tearDown(self): self.tv.destroy() support.root_withdraw() - + super().tearDown() + + def _create(self, **kwargs): + return ttk.Treeview(self.root, **kwargs) + + def test_columns(self): + widget = self.create() + self.checkParam(widget, 'columns', 'a b c', + expected=('a', 'b', 'c')) + self.checkParam(widget, 'columns', ('a', 'b', 'c')) + self.checkParam(widget, 'columns', ()) + + def test_displaycolumns(self): + widget = self.create() + widget['columns'] = ('a', 'b', 'c') + self.checkParam(widget, 'displaycolumns', 'b a c', + expected=('b', 'a', 'c')) + self.checkParam(widget, 'displaycolumns', ('b', 'a', 'c')) + self.checkParam(widget, 'displaycolumns', '#all', + expected=('#all',)) + self.checkParam(widget, 'displaycolumns', (2, 1, 0)) + self.checkInvalidParam(widget, 'displaycolumns', ('a', 'b', 'd'), + errmsg='Invalid column index d') + self.checkInvalidParam(widget, 'displaycolumns', (1, 2, 3), + errmsg='Column index 3 out of bounds') + self.checkInvalidParam(widget, 'displaycolumns', (1, -2), + errmsg='Column index -2 out of bounds') + + def test_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, -100, 0, '3c', conv=False) + self.checkPixelsParam(widget, 'height', 101.2, 102.6, conv=noconv) + + def test_selectmode(self): + widget = self.create() + self.checkEnumParam(widget, 'selectmode', + 'none', 'browse', 'extended') + + def test_show(self): + widget = self.create() + self.checkParam(widget, 'show', 'tree headings', + expected=('tree', 'headings')) + self.checkParam(widget, 'show', ('tree', 'headings')) + self.checkParam(widget, 'show', ('headings', 'tree')) + self.checkParam(widget, 'show', 'tree', expected=('tree',)) + self.checkParam(widget, 'show', 'headings', expected=('headings',)) def test_bbox(self): self.tv.pack() @@ -946,12 +1369,10 @@ class TreeviewTest(unittest.TestCase): self.assertRaises(tkinter.TclError, self.tv.heading, '#0', anchor=1) - # XXX skipping for now; should be fixed to work with newer ttk - @unittest.skip("skipping pending resolution of Issue #10734") def test_heading_callback(self): def simulate_heading_click(x, y): support.simulate_mouse_click(self.tv, x, y) - self.tv.update_idletasks() + self.tv.update() success = [] # no success for now @@ -1149,11 +1570,35 @@ class TreeviewTest(unittest.TestCase): self.assertTrue(isinstance(self.tv.tag_configure('test'), dict)) +@add_standard_options(StandardTtkOptionsTests) +class SeparatorTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'orient', 'style', 'takefocus', + # 'state'? + ) + default_orient = 'horizontal' + + def _create(self, **kwargs): + return ttk.Separator(self.root, **kwargs) + + +@add_standard_options(StandardTtkOptionsTests) +class SizegripTest(AbstractWidgetTest, unittest.TestCase): + OPTIONS = ( + 'class', 'cursor', 'style', 'takefocus', + # 'state'? + ) + + def _create(self, **kwargs): + return ttk.Sizegrip(self.root, **kwargs) + tests_gui = ( - WidgetTest, ButtonTest, CheckbuttonTest, RadiobuttonTest, - ComboboxTest, EntryTest, PanedwindowTest, ScaleTest, NotebookTest, - TreeviewTest + ButtonTest, CheckbuttonTest, ComboboxTest, EntryTest, + FrameTest, LabelFrameTest, LabelTest, MenubuttonTest, + NotebookTest, PanedWindowTest, ProgressbarTest, + RadiobuttonTest, ScaleTest, ScrollbarTest, SeparatorTest, + SizegripTest, TreeviewTest, WidgetTest, ) if __name__ == "__main__": - run_unittest(*tests_gui) + unittest.main() diff --git a/Lib/tkinter/test/widget_tests.py b/Lib/tkinter/test/widget_tests.py new file mode 100644 index 0000000000..300aa41f56 --- /dev/null +++ b/Lib/tkinter/test/widget_tests.py @@ -0,0 +1,493 @@ +# Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py + +import tkinter +from tkinter.ttk import setup_master, Scale +from tkinter.test.support import (tcl_version, requires_tcl, get_tk_patchlevel, + pixels_conv, tcl_obj_eq) + + +noconv = False +if get_tk_patchlevel() < (8, 5, 11): + noconv = str + +pixels_round = round +if get_tk_patchlevel()[:3] == (8, 5, 11): + # Issue #19085: Workaround a bug in Tk + # http://core.tcl.tk/tk/info/3497848 + pixels_round = int + + +_sentinel = object() + +class AbstractWidgetTest: + _conv_pixels = staticmethod(pixels_round) + _conv_pad_pixels = None + wantobjects = True + + def setUp(self): + self.root = setup_master() + self.scaling = float(self.root.call('tk', 'scaling')) + if not self.root.wantobjects(): + self.wantobjects = False + + def create(self, **kwargs): + widget = self._create(**kwargs) + self.addCleanup(widget.destroy) + return widget + + def assertEqual2(self, actual, expected, msg=None, eq=object.__eq__): + if eq(actual, expected): + return + self.assertEqual(actual, expected, msg) + + def checkParam(self, widget, name, value, *, expected=_sentinel, + conv=False, eq=None): + widget[name] = value + if expected is _sentinel: + expected = value + if conv: + expected = conv(expected) + if not self.wantobjects: + if isinstance(expected, tuple): + expected = tkinter._join(expected) + else: + expected = str(expected) + if eq is None: + eq = tcl_obj_eq + self.assertEqual2(widget[name], expected, eq=eq) + self.assertEqual2(widget.cget(name), expected, eq=eq) + # XXX + if not isinstance(widget, Scale): + t = widget.configure(name) + self.assertEqual(len(t), 5) + ## XXX + if not isinstance(t[4], tuple): + self.assertEqual2(t[4], expected, eq=eq) + + def checkInvalidParam(self, widget, name, value, errmsg=None, *, + keep_orig=True): + orig = widget[name] + if errmsg is not None: + errmsg = errmsg.format(value) + with self.assertRaises(tkinter.TclError) as cm: + widget[name] = value + if errmsg is not None: + self.assertEqual(str(cm.exception), errmsg) + if keep_orig: + self.assertEqual(widget[name], orig) + else: + widget[name] = orig + with self.assertRaises(tkinter.TclError) as cm: + widget.configure({name: value}) + if errmsg is not None: + self.assertEqual(str(cm.exception), errmsg) + if keep_orig: + self.assertEqual(widget[name], orig) + else: + widget[name] = orig + + def checkParams(self, widget, name, *values, **kwargs): + for value in values: + self.checkParam(widget, name, value, **kwargs) + + def checkIntegerParam(self, widget, name, *values, **kwargs): + self.checkParams(widget, name, *values, **kwargs) + self.checkInvalidParam(widget, name, '', + errmsg='expected integer but got ""') + self.checkInvalidParam(widget, name, '10p', + errmsg='expected integer but got "10p"') + self.checkInvalidParam(widget, name, 3.2, + errmsg='expected integer but got "3.2"') + + def checkFloatParam(self, widget, name, *values, conv=float, **kwargs): + for value in values: + self.checkParam(widget, name, value, conv=conv, **kwargs) + self.checkInvalidParam(widget, name, '', + errmsg='expected floating-point number but got ""') + self.checkInvalidParam(widget, name, 'spam', + errmsg='expected floating-point number but got "spam"') + + def checkBooleanParam(self, widget, name): + for value in (False, 0, 'false', 'no', 'off'): + self.checkParam(widget, name, value, expected=0) + for value in (True, 1, 'true', 'yes', 'on'): + self.checkParam(widget, name, value, expected=1) + self.checkInvalidParam(widget, name, '', + errmsg='expected boolean value but got ""') + self.checkInvalidParam(widget, name, 'spam', + errmsg='expected boolean value but got "spam"') + + def checkColorParam(self, widget, name, *, allow_empty=None, **kwargs): + self.checkParams(widget, name, + '#ff0000', '#00ff00', '#0000ff', '#123456', + 'red', 'green', 'blue', 'white', 'black', 'grey', + **kwargs) + self.checkInvalidParam(widget, name, 'spam', + errmsg='unknown color name "spam"') + + def checkCursorParam(self, widget, name, **kwargs): + self.checkParams(widget, name, 'arrow', 'watch', 'cross', '',**kwargs) + if tcl_version >= (8, 5): + self.checkParam(widget, name, 'none') + self.checkInvalidParam(widget, name, 'spam', + errmsg='bad cursor spec "spam"') + + def checkCommandParam(self, widget, name): + def command(*args): + pass + widget[name] = command + self.assertTrue(widget[name]) + self.checkParams(widget, name, '') + + def checkEnumParam(self, widget, name, *values, errmsg=None, **kwargs): + self.checkParams(widget, name, *values, **kwargs) + if errmsg is None: + errmsg2 = ' %s "{}": must be %s%s or %s' % ( + name, + ', '.join(values[:-1]), + ',' if len(values) > 2 else '', + values[-1]) + self.checkInvalidParam(widget, name, '', + errmsg='ambiguous' + errmsg2) + errmsg = 'bad' + errmsg2 + self.checkInvalidParam(widget, name, 'spam', errmsg=errmsg) + + def checkPixelsParam(self, widget, name, *values, + conv=None, keep_orig=True, **kwargs): + if conv is None: + conv = self._conv_pixels + for value in values: + expected = _sentinel + conv1 = conv + if isinstance(value, str): + if conv1 and conv1 is not str: + expected = pixels_conv(value) * self.scaling + conv1 = round + self.checkParam(widget, name, value, expected=expected, + conv=conv1, **kwargs) + self.checkInvalidParam(widget, name, '6x', + errmsg='bad screen distance "6x"', keep_orig=keep_orig) + self.checkInvalidParam(widget, name, 'spam', + errmsg='bad screen distance "spam"', keep_orig=keep_orig) + + def checkReliefParam(self, widget, name): + self.checkParams(widget, name, + 'flat', 'groove', 'raised', 'ridge', 'solid', 'sunken') + errmsg='bad relief "spam": must be '\ + 'flat, groove, raised, ridge, solid, or sunken' + if tcl_version < (8, 6): + errmsg = None + self.checkInvalidParam(widget, name, 'spam', + errmsg=errmsg) + + def checkImageParam(self, widget, name): + image = tkinter.PhotoImage('image1') + self.checkParam(widget, name, image, conv=str) + self.checkInvalidParam(widget, name, 'spam', + errmsg='image "spam" doesn\'t exist') + widget[name] = '' + + def checkVariableParam(self, widget, name, var): + self.checkParam(widget, name, var, conv=str) + + +class StandardOptionsTests: + STANDARD_OPTIONS = ( + 'activebackground', 'activeborderwidth', 'activeforeground', 'anchor', + 'background', 'bitmap', 'borderwidth', 'compound', 'cursor', + 'disabledforeground', 'exportselection', 'font', 'foreground', + 'highlightbackground', 'highlightcolor', 'highlightthickness', + 'image', 'insertbackground', 'insertborderwidth', + 'insertofftime', 'insertontime', 'insertwidth', + 'jump', 'justify', 'orient', 'padx', 'pady', 'relief', + 'repeatdelay', 'repeatinterval', + 'selectbackground', 'selectborderwidth', 'selectforeground', + 'setgrid', 'takefocus', 'text', 'textvariable', 'troughcolor', + 'underline', 'wraplength', 'xscrollcommand', 'yscrollcommand', + ) + + def test_activebackground(self): + widget = self.create() + self.checkColorParam(widget, 'activebackground') + + def test_activeborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'activeborderwidth', + 0, 1.3, 2.9, 6, -2, '10p') + + def test_activeforeground(self): + widget = self.create() + self.checkColorParam(widget, 'activeforeground') + + def test_anchor(self): + widget = self.create() + self.checkEnumParam(widget, 'anchor', + 'n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw', 'center') + + def test_background(self): + widget = self.create() + self.checkColorParam(widget, 'background') + if 'bg' in self.OPTIONS: + self.checkColorParam(widget, 'bg') + + def test_bitmap(self): + widget = self.create() + self.checkParam(widget, 'bitmap', 'questhead') + self.checkParam(widget, 'bitmap', 'gray50') + self.checkInvalidParam(widget, 'bitmap', 'spam', + errmsg='bitmap "spam" not defined') + + def test_borderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'borderwidth', + 0, 1.3, 2.6, 6, -2, '10p') + if 'bd' in self.OPTIONS: + self.checkPixelsParam(widget, 'bd', 0, 1.3, 2.6, 6, -2, '10p') + + def test_compound(self): + widget = self.create() + self.checkEnumParam(widget, 'compound', + 'bottom', 'center', 'left', 'none', 'right', 'top') + + def test_cursor(self): + widget = self.create() + self.checkCursorParam(widget, 'cursor') + + def test_disabledforeground(self): + widget = self.create() + self.checkColorParam(widget, 'disabledforeground') + + def test_exportselection(self): + widget = self.create() + self.checkBooleanParam(widget, 'exportselection') + + def test_font(self): + widget = self.create() + self.checkParam(widget, 'font', + '-Adobe-Helvetica-Medium-R-Normal--*-120-*-*-*-*-*-*') + self.checkInvalidParam(widget, 'font', '', + errmsg='font "" doesn\'t exist') + + def test_foreground(self): + widget = self.create() + self.checkColorParam(widget, 'foreground') + if 'fg' in self.OPTIONS: + self.checkColorParam(widget, 'fg') + + def test_highlightbackground(self): + widget = self.create() + self.checkColorParam(widget, 'highlightbackground') + + def test_highlightcolor(self): + widget = self.create() + self.checkColorParam(widget, 'highlightcolor') + + def test_highlightthickness(self): + widget = self.create() + self.checkPixelsParam(widget, 'highlightthickness', + 0, 1.3, 2.6, 6, '10p') + self.checkParam(widget, 'highlightthickness', -2, expected=0, + conv=self._conv_pixels) + + def test_image(self): + widget = self.create() + self.checkImageParam(widget, 'image') + + def test_insertbackground(self): + widget = self.create() + self.checkColorParam(widget, 'insertbackground') + + def test_insertborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertborderwidth', + 0, 1.3, 2.6, 6, -2, '10p') + + def test_insertofftime(self): + widget = self.create() + self.checkIntegerParam(widget, 'insertofftime', 100) + + def test_insertontime(self): + widget = self.create() + self.checkIntegerParam(widget, 'insertontime', 100) + + def test_insertwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'insertwidth', 1.3, 2.6, -2, '10p') + + def test_jump(self): + widget = self.create() + self.checkBooleanParam(widget, 'jump') + + def test_justify(self): + widget = self.create() + self.checkEnumParam(widget, 'justify', 'left', 'right', 'center', + errmsg='bad justification "{}": must be ' + 'left, right, or center') + self.checkInvalidParam(widget, 'justify', '', + errmsg='ambiguous justification "": must be ' + 'left, right, or center') + + def test_orient(self): + widget = self.create() + self.assertEqual(str(widget['orient']), self.default_orient) + self.checkEnumParam(widget, 'orient', 'horizontal', 'vertical') + + def test_padx(self): + widget = self.create() + self.checkPixelsParam(widget, 'padx', 3, 4.4, 5.6, -2, '12m', + conv=self._conv_pad_pixels) + + def test_pady(self): + widget = self.create() + self.checkPixelsParam(widget, 'pady', 3, 4.4, 5.6, -2, '12m', + conv=self._conv_pad_pixels) + + def test_relief(self): + widget = self.create() + self.checkReliefParam(widget, 'relief') + + def test_repeatdelay(self): + widget = self.create() + self.checkIntegerParam(widget, 'repeatdelay', -500, 500) + + def test_repeatinterval(self): + widget = self.create() + self.checkIntegerParam(widget, 'repeatinterval', -500, 500) + + def test_selectbackground(self): + widget = self.create() + self.checkColorParam(widget, 'selectbackground') + + def test_selectborderwidth(self): + widget = self.create() + self.checkPixelsParam(widget, 'selectborderwidth', 1.3, 2.6, -2, '10p') + + def test_selectforeground(self): + widget = self.create() + self.checkColorParam(widget, 'selectforeground') + + def test_setgrid(self): + widget = self.create() + self.checkBooleanParam(widget, 'setgrid') + + def test_state(self): + widget = self.create() + self.checkEnumParam(widget, 'state', 'active', 'disabled', 'normal') + + def test_takefocus(self): + widget = self.create() + self.checkParams(widget, 'takefocus', '0', '1', '') + + def test_text(self): + widget = self.create() + self.checkParams(widget, 'text', '', 'any string') + + def test_textvariable(self): + widget = self.create() + var = tkinter.StringVar() + self.checkVariableParam(widget, 'textvariable', var) + + def test_troughcolor(self): + widget = self.create() + self.checkColorParam(widget, 'troughcolor') + + def test_underline(self): + widget = self.create() + self.checkIntegerParam(widget, 'underline', 0, 1, 10) + + def test_wraplength(self): + widget = self.create() + self.checkPixelsParam(widget, 'wraplength', 100) + + def test_xscrollcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'xscrollcommand') + + def test_yscrollcommand(self): + widget = self.create() + self.checkCommandParam(widget, 'yscrollcommand') + + # non-standard but common options + + def test_command(self): + widget = self.create() + self.checkCommandParam(widget, 'command') + + def test_indicatoron(self): + widget = self.create() + self.checkBooleanParam(widget, 'indicatoron') + + def test_offrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'offrelief') + + def test_overrelief(self): + widget = self.create() + self.checkReliefParam(widget, 'overrelief') + + def test_selectcolor(self): + widget = self.create() + self.checkColorParam(widget, 'selectcolor') + + def test_selectimage(self): + widget = self.create() + self.checkImageParam(widget, 'selectimage') + + @requires_tcl(8, 5) + def test_tristateimage(self): + widget = self.create() + self.checkImageParam(widget, 'tristateimage') + + @requires_tcl(8, 5) + def test_tristatevalue(self): + widget = self.create() + self.checkParam(widget, 'tristatevalue', 'unknowable') + + def test_variable(self): + widget = self.create() + var = tkinter.DoubleVar() + self.checkVariableParam(widget, 'variable', var) + + +class IntegerSizeTests: + def test_height(self): + widget = self.create() + self.checkIntegerParam(widget, 'height', 100, -100, 0) + + def test_width(self): + widget = self.create() + self.checkIntegerParam(widget, 'width', 402, -402, 0) + + +class PixelSizeTests: + def test_height(self): + widget = self.create() + self.checkPixelsParam(widget, 'height', 100, 101.2, 102.6, -100, 0, '3c') + + def test_width(self): + widget = self.create() + self.checkPixelsParam(widget, 'width', 402, 403.4, 404.6, -402, 0, '5i') + + +def add_standard_options(*source_classes): + # This decorator adds test_xxx methods from source classes for every xxx + # option in the OPTIONS class attribute if they are not defined explicitly. + def decorator(cls): + for option in cls.OPTIONS: + methodname = 'test_' + option + if not hasattr(cls, methodname): + for source_class in source_classes: + if hasattr(source_class, methodname): + setattr(cls, methodname, + getattr(source_class, methodname)) + break + else: + def test(self, option=option): + widget = self.create() + widget[option] + raise AssertionError('Option "%s" is not tested in %s' % + (option, cls.__name__)) + test.__name__ = methodname + setattr(cls, methodname, test) + return cls + return decorator diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py index d171451f2c..b8cb0c76b7 100644 --- a/Lib/unittest/test/test_case.py +++ b/Lib/unittest/test/test_case.py @@ -307,7 +307,7 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): def test(self): pass - self.assertTrue(Foo('test').failureException is AssertionError) + self.assertIs(Foo('test').failureException, AssertionError) # "This class attribute gives the exception raised by the test() method. # If a test framework needs to use a specialized exception, possibly to @@ -325,7 +325,7 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): failureException = RuntimeError - self.assertTrue(Foo('test').failureException is RuntimeError) + self.assertIs(Foo('test').failureException, RuntimeError) Foo('test').run(result) @@ -348,7 +348,7 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): failureException = RuntimeError - self.assertTrue(Foo('test').failureException is RuntimeError) + self.assertIs(Foo('test').failureException, RuntimeError) Foo('test').run(result) @@ -660,7 +660,7 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): msg = e.args[0] else: self.fail('assertSequenceEqual did not fail.') - self.assertTrue(len(msg) < len(diff)) + self.assertLess(len(msg), len(diff)) self.assertIn(omitted, msg) self.maxDiff = len(diff) * 2 @@ -670,7 +670,7 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): msg = e.args[0] else: self.fail('assertSequenceEqual did not fail.') - self.assertTrue(len(msg) > len(diff)) + self.assertGreater(len(msg), len(diff)) self.assertNotIn(omitted, msg) self.maxDiff = None @@ -680,7 +680,7 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): msg = e.args[0] else: self.fail('assertSequenceEqual did not fail.') - self.assertTrue(len(msg) > len(diff)) + self.assertGreater(len(msg), len(diff)) self.assertNotIn(omitted, msg) def testTruncateMessage(self): diff --git a/Lib/unittest/test/test_loader.py b/Lib/unittest/test/test_loader.py index f1f8ecd978..fcd2e07624 100644 --- a/Lib/unittest/test/test_loader.py +++ b/Lib/unittest/test/test_loader.py @@ -1305,4 +1305,4 @@ class Test_TestLoader(unittest.TestCase): # "The default value is the TestSuite class" def test_suiteClass__default_value(self): loader = unittest.TestLoader() - self.assertTrue(loader.suiteClass is unittest.TestSuite) + self.assertIs(loader.suiteClass, unittest.TestSuite) diff --git a/Lib/unittest/test/test_result.py b/Lib/unittest/test/test_result.py index 1c58e61bb2..7d40725cf2 100644 --- a/Lib/unittest/test/test_result.py +++ b/Lib/unittest/test/test_result.py @@ -176,7 +176,7 @@ class Test_TestResult(unittest.TestCase): self.assertEqual(result.shouldStop, False) test_case, formatted_exc = result.failures[0] - self.assertTrue(test_case is test) + self.assertIs(test_case, test) self.assertIsInstance(formatted_exc, str) # "addError(test, err)" @@ -224,7 +224,7 @@ class Test_TestResult(unittest.TestCase): self.assertEqual(result.shouldStop, False) test_case, formatted_exc = result.errors[0] - self.assertTrue(test_case is test) + self.assertIs(test_case, test) self.assertIsInstance(formatted_exc, str) def testGetDescriptionWithoutDocstring(self): diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 8bfb29391d..a362a2f784 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -177,7 +177,7 @@ class CallTest(unittest.TestCase): args = _Call(((1, 2, 3), {})) self.assertEqual(args, call(1, 2, 3)) self.assertEqual(call(1, 2, 3), args) - self.assertTrue(call(1, 2, 3) in [args]) + self.assertIn(call(1, 2, 3), [args]) def test_call_ne(self): @@ -793,7 +793,7 @@ class SpecSignatureTest(unittest.TestCase): mock_property = foo.foo # no spec on properties - self.assertTrue(isinstance(mock_property, MagicMock)) + self.assertIsInstance(mock_property, MagicMock) mock_property(1, 2, 3) mock_property.abc(4, 5, 6) mock_property.assert_called_once_with(1, 2, 3) @@ -826,19 +826,19 @@ class TestCallList(unittest.TestCase): mock(b=6) for kall in call(1, 2), call(a=3), call(3, 4), call(b=6): - self.assertTrue(kall in mock.call_args_list) + self.assertIn(kall, mock.call_args_list) calls = [call(a=3), call(3, 4)] - self.assertTrue(calls in mock.call_args_list) + self.assertIn(calls, mock.call_args_list) calls = [call(1, 2), call(a=3)] - self.assertTrue(calls in mock.call_args_list) + self.assertIn(calls, mock.call_args_list) calls = [call(3, 4), call(b=6)] - self.assertTrue(calls in mock.call_args_list) + self.assertIn(calls, mock.call_args_list) calls = [call(3, 4)] - self.assertTrue(calls in mock.call_args_list) + self.assertIn(calls, mock.call_args_list) - self.assertFalse(call('fish') in mock.call_args_list) - self.assertFalse([call('fish')] in mock.call_args_list) + self.assertNotIn(call('fish'), mock.call_args_list) + self.assertNotIn([call('fish')], mock.call_args_list) def test_call_list_str(self): diff --git a/Lib/unittest/test/testmock/testmagicmethods.py b/Lib/unittest/test/testmock/testmagicmethods.py index 2bcf08801e..5ff158dd12 100644 --- a/Lib/unittest/test/testmock/testmagicmethods.py +++ b/Lib/unittest/test/testmock/testmagicmethods.py @@ -37,12 +37,12 @@ class TestMockingMagicMethods(unittest.TestCase): return self, 'fish' mock.__getitem__ = f - self.assertFalse(mock.__getitem__ is f) + self.assertIsNot(mock.__getitem__, f) self.assertEqual(mock['foo'], (mock, 'fish')) self.assertEqual(mock.__getitem__('foo'), (mock, 'fish')) mock.__getitem__ = mock - self.assertTrue(mock.__getitem__ is mock) + self.assertIs(mock.__getitem__, mock) def test_magic_methods_isolated_between_mocks(self): @@ -212,8 +212,8 @@ class TestMockingMagicMethods(unittest.TestCase): self.assertEqual(len(mock), 6) mock.__contains__ = lambda s, o: o == 3 - self.assertTrue(3 in mock) - self.assertFalse(6 in mock) + self.assertIn(3, mock) + self.assertNotIn(6, mock) mock.__iter__ = lambda s: iter('foobarbaz') self.assertEqual(list(mock), list('foobarbaz')) diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 3d0776c16f..cef5405fe9 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -52,7 +52,7 @@ class MockTest(unittest.TestCase): "method_calls not initialised correctly") # Can't use hasattr for this test as it always returns True on a mock - self.assertFalse('_items' in mock.__dict__, + self.assertNotIn('_items', mock.__dict__, "default mock should not have '_items' attribute") self.assertIsNone(mock._mock_parent, @@ -493,19 +493,19 @@ class MockTest(unittest.TestCase): pass mock = Mock(spec=X) - self.assertTrue(isinstance(mock, X)) + self.assertIsInstance(mock, X) mock = Mock(spec=X()) - self.assertTrue(isinstance(mock, X)) + self.assertIsInstance(mock, X) self.assertIs(mock.__class__, X) self.assertEqual(Mock().__class__.__name__, 'Mock') mock = Mock(spec_set=X) - self.assertTrue(isinstance(mock, X)) + self.assertIsInstance(mock, X) mock = Mock(spec_set=X()) - self.assertTrue(isinstance(mock, X)) + self.assertIsInstance(mock, X) def test_setting_attribute_with_spec_set(self): diff --git a/Lib/unittest/test/testmock/testsentinel.py b/Lib/unittest/test/testmock/testsentinel.py index bfda68ece3..3fb5acbc25 100644 --- a/Lib/unittest/test/testmock/testsentinel.py +++ b/Lib/unittest/test/testmock/testsentinel.py @@ -17,7 +17,7 @@ class SentinelTest(unittest.TestCase): def testDEFAULT(self): - self.assertTrue(DEFAULT is sentinel.DEFAULT) + self.assertIs(DEFAULT, sentinel.DEFAULT) def testBases(self): # If this doesn't raise an AttributeError then help(mock) is broken diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index fc5b65b2ad..3606370e86 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -38,7 +38,7 @@ logger = logging.getLogger(__name__) class EnvBuilder: """ This class exists to allow virtual environment creation to be - customised. The constructor parameters determine the builder's + customized. The constructor parameters determine the builder's behaviour when called upon to create a virtual environment. By default, the builder makes the system (global) site-packages dir @@ -336,7 +336,7 @@ def main(args=None): elif not hasattr(sys, 'base_prefix'): compatible = False if not compatible: - raise ValueError('This script is only for use with Python 3.3') + raise ValueError('This script is only for use with Python >= 3.3') else: import argparse diff --git a/Lib/wave.py b/Lib/wave.py index a1daead8ab..6285c74876 100644 --- a/Lib/wave.py +++ b/Lib/wave.py @@ -82,15 +82,16 @@ WAVE_FORMAT_PCM = 0x0001 _array_fmts = None, 'b', 'h', None, 'i' -# Determine endian-ness import struct -if struct.pack("h", 1) == b"\000\001": - big_endian = 1 -else: - big_endian = 0 - +import sys from chunk import Chunk +def _byteswap3(data): + ba = bytearray(data) + ba[::3] = data[2::3] + ba[2::3] = data[::3] + return bytes(ba) + class Wave_read: """Variables used in this class: @@ -231,7 +232,7 @@ class Wave_read: self._data_seek_needed = 0 if nframes == 0: return b'' - if self._sampwidth > 1 and big_endian: + if self._sampwidth in (2, 4) and sys.byteorder == 'big': # unfortunately the fromfile() method does not take # something that only looks like a file object, so # we have to reach into the innards of the chunk object @@ -252,6 +253,8 @@ class Wave_read: data = data.tobytes() else: data = self._data_chunk.read(nframes * self._framesize) + if self._sampwidth == 3 and sys.byteorder == 'big': + data = _byteswap3(data) if self._convert and data: data = self._convert(data) self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth) @@ -419,7 +422,7 @@ class Wave_write: nframes = len(data) // (self._sampwidth * self._nchannels) if self._convert: data = self._convert(data) - if self._sampwidth > 1 and big_endian: + if self._sampwidth in (2, 4) and sys.byteorder == 'big': import array data = array.array(_array_fmts[self._sampwidth], data) assert data.itemsize == self._sampwidth @@ -427,6 +430,8 @@ class Wave_write: data.tofile(self._file) self._datawritten = self._datawritten + len(data) * self._sampwidth else: + if self._sampwidth == 3 and sys.byteorder == 'big': + data = _byteswap3(data) self._file.write(data) self._datawritten = self._datawritten + len(data) self._nframeswritten = self._nframeswritten + nframes diff --git a/Lib/xml/dom/minicompat.py b/Lib/xml/dom/minicompat.py index 62725c6198..1244500259 100644 --- a/Lib/xml/dom/minicompat.py +++ b/Lib/xml/dom/minicompat.py @@ -1,4 +1,8 @@ -"""Python version compatibility support for minidom.""" +"""Python version compatibility support for minidom. + +This module contains internal implementation details and +should not be imported; use xml.dom.minidom instead. +""" # This module should only be imported using "import *". # diff --git a/Makefile.pre.in b/Makefile.pre.in index 27dd75fb70..5f677c803f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1048,7 +1048,8 @@ LIBSUBDIRS= tkinter tkinter/test tkinter/test/test_tkinter \ lib2to3 lib2to3/fixes lib2to3/pgen2 lib2to3/tests \ lib2to3/tests/data lib2to3/tests/data/fixers \ lib2to3/tests/data/fixers/myfixes \ - ctypes ctypes/test ctypes/macholib idlelib idlelib/Icons \ + ctypes ctypes/test ctypes/macholib \ + idlelib idlelib/Icons idlelib/idle_test \ distutils distutils/command distutils/tests $(XMLLIBSUBDIRS) \ importlib test/test_importlib test/test_importlib/builtin \ test/test_importlib/extension test/test_importlib/frozen \ @@ -2,6 +2,61 @@ Python News +++++++++++ +What's New in Python 3.3.4 release candidate 1? +=============================================== + +*Not yet released, see sections below for changes released in 3.3.3* + +Core and Builtins +----------------- + +Library +------- + +- Issue #19523: Closed FileHandler leak which occurred when delay was set. + +- Issue #13674: Prevented time.strftime from crashing on Windows when given + a year before 1900 and a format of %y. + +- Issue #19544 and Issue #6286: Restore use of urllib over http allowing use + of http_proxy for Distutils upload command, a feature accidentally lost + in the rollback of distutils2. + +- Issue #19544 and Issue #7457: Restore the read_pkg_file method to + distutils.dist.DistributionMetadata accidentally removed in the undo of + distutils2. + +- Issue #1575020: Fixed support of 24-bit wave files on big-endian platforms. + +- Issue #19480: HTMLParser now accepts all valid start-tag names as defined + by the HTML5 standard. + +- Issue #6157: Fixed tkinter.Text.debug(). Original patch by Guilherme Polo. + +- Issue #6160: The bbox() method of tkinter.Spinbox now returns a tuple of + integers instead of a string. Based on patch by Guilherme Polo. + +- Issue #10197: Rework subprocess.get[status]output to use subprocess + functionality and thus to work on Windows. Patch by Nick Coghlan. + +- Issue #19286: Directories in ``package_data`` are no longer added to + the filelist, preventing failure outlined in the ticket. + +Tests +----- + +- Issue #19440: Clean up test_capi by removing an unnecessary __future__ + import, converting from test_main to unittest.main, and running the + _testcapi module tests within a unittest TestCase. + +- Issue #18702: All skipped tests now reported as skipped. + +- Issue #19085: Added basic tests for all tkinter widget options. + +Build +----- + + What's New in Python 3.3.3? =========================== @@ -114,6 +169,10 @@ Core and Builtins Library ------- +- Issue #19395: Raise an exception when attempting to pickle a bz2 or lzma + compressor/decompressor object, rather than creating a pickle that would + cause a segfault when loaded and used. + - Issue #19227: Try to fix deadlocks caused by re-seeding then OpenSSL pseudo-random number generator on fork(). diff --git a/Misc/valgrind-python.supp b/Misc/valgrind-python.supp index 81a07c9f45..e612555b6d 100644 --- a/Misc/valgrind-python.supp +++ b/Misc/valgrind-python.supp @@ -456,6 +456,15 @@ fun:PyUnicode_FSConverter } +{ + wcscmp_false_positive + Memcheck:Addr8 + fun:wcscmp + fun:_PyOS_GetOpt + fun:Py_Main + fun:main +} + # Additional suppressions for the unified decimal tests: { test_decimal diff --git a/Modules/_bz2module.c b/Modules/_bz2module.c index 4eee5a2fee..3f7a6cf026 100644 --- a/Modules/_bz2module.c +++ b/Modules/_bz2module.c @@ -250,6 +250,14 @@ BZ2Compressor_flush(BZ2Compressor *self, PyObject *noargs) return result; } +static PyObject * +BZ2Compressor_getstate(BZ2Compressor *self, PyObject *noargs) +{ + PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object", + Py_TYPE(self)->tp_name); + return NULL; +} + static int BZ2Compressor_init(BZ2Compressor *self, PyObject *args, PyObject *kwargs) { @@ -298,10 +306,11 @@ BZ2Compressor_dealloc(BZ2Compressor *self) } static PyMethodDef BZ2Compressor_methods[] = { - {"compress", (PyCFunction)BZ2Compressor_compress, METH_VARARGS, + {"compress", (PyCFunction)BZ2Compressor_compress, METH_VARARGS, BZ2Compressor_compress__doc__}, - {"flush", (PyCFunction)BZ2Compressor_flush, METH_NOARGS, + {"flush", (PyCFunction)BZ2Compressor_flush, METH_NOARGS, BZ2Compressor_flush__doc__}, + {"__getstate__", (PyCFunction)BZ2Compressor_getstate, METH_NOARGS}, {NULL} }; @@ -452,6 +461,14 @@ BZ2Decompressor_decompress(BZ2Decompressor *self, PyObject *args) return result; } +static PyObject * +BZ2Decompressor_getstate(BZ2Decompressor *self, PyObject *noargs) +{ + PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object", + Py_TYPE(self)->tp_name); + return NULL; +} + static int BZ2Decompressor_init(BZ2Decompressor *self, PyObject *args, PyObject *kwargs) { @@ -502,6 +519,7 @@ BZ2Decompressor_dealloc(BZ2Decompressor *self) static PyMethodDef BZ2Decompressor_methods[] = { {"decompress", (PyCFunction)BZ2Decompressor_decompress, METH_VARARGS, BZ2Decompressor_decompress__doc__}, + {"__getstate__", (PyCFunction)BZ2Decompressor_getstate, METH_NOARGS}, {NULL} }; diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index d3e394041f..06c2c395b8 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -3009,18 +3009,24 @@ convert_op_cmp(PyObject **vcmp, PyObject **wcmp, PyObject *v, PyObject *w, *wcmp = Py_NotImplemented; } } - else if (PyObject_IsInstance(w, Rational)) { - *wcmp = numerator_as_decimal(w, context); - if (*wcmp && !mpd_isspecial(MPD(v))) { - *vcmp = multiply_by_denominator(v, w, context); - if (*vcmp == NULL) { - Py_CLEAR(*wcmp); + else { + int is_rational = PyObject_IsInstance(w, Rational); + if (is_rational < 0) { + *wcmp = NULL; + } + else if (is_rational > 0) { + *wcmp = numerator_as_decimal(w, context); + if (*wcmp && !mpd_isspecial(MPD(v))) { + *vcmp = multiply_by_denominator(v, w, context); + if (*vcmp == NULL) { + Py_CLEAR(*wcmp); + } } } - } - else { - Py_INCREF(Py_NotImplemented); - *wcmp = Py_NotImplemented; + else { + Py_INCREF(Py_NotImplemented); + *wcmp = Py_NotImplemented; + } } if (*wcmp == NULL || *wcmp == Py_NotImplemented) { @@ -3102,6 +3108,7 @@ dec_strdup(const char *src, Py_ssize_t size) { char *dest = PyMem_Malloc(size+1); if (dest == NULL) { + PyErr_NoMemory(); return NULL; } diff --git a/Modules/_lzmamodule.c b/Modules/_lzmamodule.c index b482a7767d..643616052c 100644 --- a/Modules/_lzmamodule.c +++ b/Modules/_lzmamodule.c @@ -546,6 +546,14 @@ Compressor_flush(Compressor *self, PyObject *noargs) return result; } +static PyObject * +Compressor_getstate(Compressor *self, PyObject *noargs) +{ + PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object", + Py_TYPE(self)->tp_name); + return NULL; +} + static int Compressor_init_xz(lzma_stream *lzs, int check, uint32_t preset, PyObject *filterspecs) @@ -712,6 +720,7 @@ static PyMethodDef Compressor_methods[] = { Compressor_compress_doc}, {"flush", (PyCFunction)Compressor_flush, METH_NOARGS, Compressor_flush_doc}, + {"__getstate__", (PyCFunction)Compressor_getstate, METH_NOARGS}, {NULL} }; @@ -869,6 +878,14 @@ Decompressor_decompress(Decompressor *self, PyObject *args) return result; } +static PyObject * +Decompressor_getstate(Decompressor *self, PyObject *noargs) +{ + PyErr_Format(PyExc_TypeError, "cannot serialize '%s' object", + Py_TYPE(self)->tp_name); + return NULL; +} + static int Decompressor_init_raw(lzma_stream *lzs, PyObject *filterspecs) { @@ -991,6 +1008,7 @@ Decompressor_dealloc(Decompressor *self) static PyMethodDef Decompressor_methods[] = { {"decompress", (PyCFunction)Decompressor_decompress, METH_VARARGS, Decompressor_decompress_doc}, + {"__getstate__", (PyCFunction)Decompressor_getstate, METH_NOARGS}, {NULL} }; diff --git a/Modules/audioop.c b/Modules/audioop.c index 7e40bbddc6..3f047623eb 100644 --- a/Modules/audioop.c +++ b/Modules/audioop.c @@ -27,7 +27,8 @@ typedef short PyInt16; #endif static const int maxvals[] = {0, 0x7F, 0x7FFF, 0x7FFFFF, 0x7FFFFFFF}; -static const int minvals[] = {0, -0x80, -0x8000, -0x800000, -0x80000000}; +/* -1 trick is needed on Windows to support -0x80000000 without a warning */ +static const int minvals[] = {0, -0x80, -0x8000, -0x800000, -0x7FFFFFFF-1}; static const unsigned int masks[] = {0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF}; static int @@ -385,7 +386,9 @@ audioop_minmax(PyObject *self, PyObject *args) signed char *cp; Py_ssize_t len, i; int size, val = 0; - int min = 0x7fffffff, max = -0x80000000; + /* -1 trick below is needed on Windows to support -0x80000000 without + a warning */ + int min = 0x7fffffff, max = -0x7FFFFFFF-1; if (!PyArg_ParseTuple(args, "s#i:minmax", &cp, &len, &size)) return NULL; diff --git a/Modules/binascii.c b/Modules/binascii.c index 340ec9c426..a84badc7c9 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -1129,7 +1129,8 @@ binascii_hexlify(PyObject *self, PyObject *args) PyDoc_STRVAR(doc_hexlify, "b2a_hex(data) -> s; Hexadecimal representation of binary data.\n\ \n\ -This function is also available as \"hexlify()\"."); +The return value is a bytes object. This function is also\n\ +available as \"hexlify()\"."); static int diff --git a/Modules/fcntlmodule.c b/Modules/fcntlmodule.c index de1e0daa30..6769098203 100644 --- a/Modules/fcntlmodule.c +++ b/Modules/fcntlmodule.c @@ -27,7 +27,7 @@ conv_descriptor(PyObject *object, int *target) } -/* fcntl(fd, opt, [arg]) */ +/* fcntl(fd, op, [arg]) */ static PyObject * fcntl_fcntl(PyObject *self, PyObject *args) @@ -77,11 +77,12 @@ fcntl_fcntl(PyObject *self, PyObject *args) } PyDoc_STRVAR(fcntl_doc, -"fcntl(fd, opt, [arg])\n\ +"fcntl(fd, op, [arg])\n\ \n\ -Perform the requested operation on file descriptor fd. The operation\n\ -is defined by op and is operating system dependent. These constants are\n\ -available from the fcntl module. The argument arg is optional, and\n\ +Perform the operation op on file descriptor fd. The values used\n\ +for op are operating system dependent, and are available\n\ +as constants in the fcntl module, using the same names as used in\n\ +the relevant C header files. The argument arg is optional, and\n\ defaults to 0; it may be an int or a string. If arg is given as a string,\n\ the return value of fcntl is a string of that length, containing the\n\ resulting value put in the arg buffer by the operating system. The length\n\ @@ -90,7 +91,7 @@ is an integer or if none is specified, the result value is an integer\n\ corresponding to the return value of the fcntl call in the C code."); -/* ioctl(fd, opt, [arg]) */ +/* ioctl(fd, op, [arg]) */ static PyObject * fcntl_ioctl(PyObject *self, PyObject *args) @@ -104,7 +105,7 @@ fcntl_ioctl(PyObject *self, PyObject *args) whereas the system expects it to be a 32bit bit field value regardless of it being passed as an int or unsigned long on various platforms. See the termios.TIOCSWINSZ constant across - platforms for an example of thise. + platforms for an example of this. If any of the 64bit platforms ever decide to use more than 32bits in their unsigned long ioctl codes this will break and need @@ -222,11 +223,12 @@ fcntl_ioctl(PyObject *self, PyObject *args) } PyDoc_STRVAR(ioctl_doc, -"ioctl(fd, opt[, arg[, mutate_flag]])\n\ +"ioctl(fd, op[, arg[, mutate_flag]])\n\ \n\ -Perform the requested operation on file descriptor fd. The operation is\n\ -defined by opt and is operating system dependent. Typically these codes are\n\ -retrieved from the fcntl or termios library modules.\n\ +Perform the operation op on file descriptor fd. The values used for op\n\ +are operating system dependent, and are available as constants in the\n\ +fcntl or termios library modules, using the same names as used in the\n\ +relevant C header files.\n\ \n\ The argument arg is optional, and defaults to 0; it may be an int or a\n\ buffer containing character data (most likely a string or an array). \n\ diff --git a/Modules/timemodule.c b/Modules/timemodule.c index 03476d97dc..42ec3662e8 100644 --- a/Modules/timemodule.c +++ b/Modules/timemodule.c @@ -632,6 +632,13 @@ time_strftime(PyObject *self, PyObject *args) Py_DECREF(format); return NULL; } + if ((outbuf[1] == 'y') && buf.tm_year < 0) + { + PyErr_SetString(PyExc_ValueError, + "format %y requires year >= 1900 on Windows"); + Py_DECREF(format); + return NULL; + } } #endif diff --git a/PC/VS9.0/x64.vsprops b/PC/VS9.0/x64.vsprops index 9f88d443fe..d06f470ceb 100644 --- a/PC/VS9.0/x64.vsprops +++ b/PC/VS9.0/x64.vsprops @@ -8,7 +8,7 @@ >
<Tool
Name="VCCLCompilerTool"
- AdditionalOptions="/USECL:MS_OPTERON /GS-"
+ AdditionalOptions="/GS-"
PreprocessorDefinitions="_WIN64;_M_X64"
/>
<Tool
diff --git a/PC/launcher.c b/PC/launcher.c index 445e96e573..216a562603 100644 --- a/PC/launcher.c +++ b/PC/launcher.c @@ -807,10 +807,10 @@ parse_shebang(wchar_t * shebang_line, int nchars, wchar_t ** command, } if (*vpp == NULL) { /* - * Not found in builtins - look in customised commands. + * Not found in builtins - look in customized commands. * * We can't permanently modify the shebang line in case - * it's not a customised command, but we can temporarily + * it's not a customized command, but we can temporarily * stick a NUL after the command while searching for it, * then put back the char we zapped. */ diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index fc259557a9..6f07689ce5 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -1,8 +1,8 @@ Building Python using VC++ 10.0 ------------------------------- -This directory is used to build Python for Win32 and x64 platforms, e.g. -Windows 2000, XP, Vista and Windows Server 2008. In order to build 32-bit +This directory is used to build Python for Win32 and x64 platforms, e.g. +Windows XP, Vista and Windows Server 2008. In order to build 32-bit debug and release executables, Microsoft Visual C++ 2010 Express Edition is required at the very least. In order to build 64-bit debug and release executables, Visual Studio 2010 Standard Edition is required at the very @@ -27,7 +27,7 @@ won't stop you from building Python. The solution is configured to build the projects in the correct order. "Build Solution" or F7 takes care of dependencies except for x64 builds. To make -cross compiling x64 builds on a 32bit OS possible the x64 builds require a +cross compiling x64 builds on a 32bit OS possible the x64 builds require a 32bit version of Python. NOTE: @@ -47,7 +47,7 @@ optimization end up in their own folders, too. Legacy support -------------- -You can find build directories for older versions of Visual Studio and +You can find build directories for older versions of Visual Studio and Visual C++ in the PC directory. The legacy build directories are no longer actively maintained and may not work out of the box. @@ -64,10 +64,10 @@ PC/VS9.0/ C RUNTIME --------- -Visual Studio 2010 uses version 10 of the C runtime (MSVCRT9). The executables +Visual Studio 2010 uses version 10 of the C runtime (MSVCRT10). The executables no longer use the "Side by Side" assemblies used in previous versions of the compiler. This simplifies distribution of applications. -The run time libraries are avalible under the VC/Redist folder of your visual studio +The run time libraries are available under the VC/Redist folder of your visual studio distribution. For more info, see the Readme in the VC/Redist folder. SUBPROJECTS @@ -103,14 +103,14 @@ winsound Python-controlled subprojects that wrap external projects: _sqlite3 - Wraps SQLite 3.7.4, which is currently built by sqlite3.vcproj (see below). + Wraps SQLite 3.7.12, which is currently built by sqlite3.vcxproj. _tkinter Wraps the Tk windowing system. Unlike _sqlite3, there's no - corresponding tcltk.vcproj-type project that builds Tcl/Tk from vcproj's + corresponding tcltk.vcxproj-type project that builds Tcl/Tk from vcxproj's within our pcbuild.sln, which means this module expects to find a pre-built Tcl/Tk in either ..\..\tcltk for 32-bit or ..\..\tcltk64 for 64-bit (relative to this directory). See below for instructions to build - Tcl/Tk. + Tcl/Tk. _bz2 Python wrapper for the libbzip2 compression library. Homepage http://www.bzip.org/ @@ -122,16 +122,6 @@ _bz2 ** NOTE: if you use the Tools\buildbot\external(-amd64).bat approach for obtaining external sources then you don't need to manually get the source above via subversion. ** - - A custom pre-link step in the bz2 project settings should manage to - build bzip2-1.0.6\libbz2.lib by magic before bz2.pyd (or bz2_d.pyd) is - linked in PCbuild\. - However, the bz2 project is not smart enough to remove anything under - bzip2-1.0.6\ when you do a clean, so if you want to rebuild bzip2.lib - you need to clean up bzip2-1.0.6\ by hand. - - All of this managed to build libbz2.lib in - bzip2-1.0.6\$platform-$configuration\, which the Python project links in. _lzma Python wrapper for the liblzma compression library. @@ -156,21 +146,19 @@ _ssl You must install the NASM assembler 2.10 or newer from http://nasm.sf.net - for x86 builds. Put nasmw.exe anywhere in your PATH. More recent + for x86 builds. Put nasm.exe anywhere in your PATH. More recent versions of OpenSSL may need a later version of NASM. If OpenSSL's self tests don't pass, you should first try to update NASM and do a full rebuild of OpenSSL. - Note: recent releases of nasm only have nasm.exe. Just rename it to - nasmw.exe. You can also install ActivePerl from http://www.activestate.com/activeperl/ - if you like to use the official sources instead of the files from + if you like to use the official sources instead of the files from python's subversion repository. The svn version contains pre-build makefiles and assembly files. The build process makes sure that no patented algorithms are included. - For now RC5, MDC2 and IDEA are excluded from the build. You may have + For now RC5, MDC2 and IDEA are excluded from the build. You may have to manually remove $(OBJ_D)\i_*.obj from ms\nt.mak if the build process complains about missing files or forbidden IDEA. Again the files provided in the subversion repository are already fixed. @@ -191,16 +179,16 @@ _ssl this by hand. The subprojects above wrap external projects Python doesn't control, and as -such, a little more work is required in order to download the relevant source +such, a little more work is required in order to download the relevant source files for each project before they can be built. The buildbots do this each -time they're built, so the easiest approach is to run either external.bat or +time they're built, so the easiest approach is to run either external.bat or external-amd64.bat in the ..\Tools\buildbot directory from ..\, i.e.: C:\..\svn.python.org\projects\python\trunk\PCbuild>cd .. C:\..\svn.python.org\projects\python\trunk>Tools\buildbot\external.bat This extracts all the external subprojects from http://svn.python.org/external -via Subversion (so you'll need an svn.exe on your PATH) and places them in +via Subversion (so you'll need an svn.exe on your PATH) and places them in ..\.. (relative to this directory). The external(-amd64).bat scripts will also build a debug build of Tcl/Tk; there aren't any equivalent batch files for building release versions of Tcl/Tk lying around in the Tools\buildbot @@ -209,18 +197,18 @@ though, take a look at the relevant external(-amd64).bat file and find the two nmake lines, then call each one without the 'DEBUG=1' parameter, i.e.: The external-amd64.bat file contains this for tcl: - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 clean all install + nmake -f makefile.vc DEBUG=1 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 clean all install So for a release build, you'd call it as: - nmake -f makefile.vc COMPILERFLAGS=-DWINVER=0x0500 MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 clean all install + nmake -f makefile.vc MACHINE=AMD64 INSTALLDIR=..\..\tcltk64 clean all install XXX Should we compile with OPTS=threads? XXX Our installer copies a lot of stuff out of the Tcl/Tk install XXX directory. Is all of that really needed for Python use of Tcl/Tk? This will be cleaned up in the future; ideally Tcl/Tk will be brought into our -pcbuild.sln as custom .vcproj files, just as we've recently done with the -sqlite3.vcproj file, which will remove the need for Tcl/Tk to be built +pcbuild.sln as custom .vcxproj files, just as we've recently done with the +sqlite3.vcxproj file, which will remove the need for Tcl/Tk to be built separately via a batch file. XXX trent.nelson 02-Apr-08: @@ -243,7 +231,7 @@ XXX trent.nelson 02-Apr-08: junction as follows (using the directory structure above as an example): C:\..\python\trunk\external <- already exists and has built versions - of the external subprojects + of the external subprojects C:\..\python\branches\py3k>linkd.exe external ..\..\trunk\external Link created at: external @@ -256,19 +244,9 @@ XXX trent.nelson 02-Apr-08: Building for Itanium -------------------- -NOTE: Official support for Itanium builds have been dropped from the build. Please contact us and provide patches if you are interested in Itanium builds. -The project files support a ReleaseItanium configuration which creates -Win64/Itanium binaries. For this to work, you need to install the Platform -SDK, in particular the 64-bit support. This includes an Itanium compiler -(future releases of the SDK likely include an AMD64 compiler as well). -In addition, you need the Visual Studio plugin for external C compilers, -from http://sf.net/projects/vsextcomp. The plugin will wrap cl.exe, to -locate the proper target compiler, and convert compiler options -accordingly. The project files require at least version 0.9. - Building for AMD64 ------------------ @@ -288,7 +266,7 @@ Profile Guided Optimization The solution has two configurations for PGO. The PGInstrument configuration must be build first. The PGInstrument binaries are -lniked against a profiling library and contain extra debug +linked against a profiling library and contain extra debug information. The PGUpdate configuration takes the profiling data and generates optimized binaries. @@ -296,23 +274,23 @@ The build_pgo.bat script automates the creation of optimized binaries. It creates the PGI files, runs the unit test suite or PyBench with the PGI python and finally creates the optimized files. -http://msdn2.microsoft.com/en-us/library/e7k32f4k(VS.90).aspx +http://msdn.microsoft.com/en-us/library/e7k32f4k(VS.100).aspx Static library -------------- The solution has no configuration for static libraries. However it is easy -it build a static library instead of a DLL. You simply have to set the +it build a static library instead of a DLL. You simply have to set the "Configuration Type" to "Static Library (.lib)" and alter the preprocessor macro "Py_ENABLE_SHARED" to "Py_NO_ENABLE_SHARED". You may also have to -change the "Runtime Library" from "Multi-threaded DLL (/MD)" to +change the "Runtime Library" from "Multi-threaded DLL (/MD)" to "Multi-threaded (/MT)". Visual Studio properties ------------------------ -The PCbuild solution makes heavy use of Visual Studio property files -(*.vsprops). The properties can be viewed and altered in the Property +The PCbuild solution makes heavy use of Visual Studio property files +(*.props). The properties can be viewed and altered in the Property Manager (View -> Other Windows -> Property Manager). * debug (debug macro: _DEBUG) diff --git a/PCbuild/ssl.vcxproj b/PCbuild/ssl.vcxproj index 41fd780c3b..d5eac9ae04 100644 --- a/PCbuild/ssl.vcxproj +++ b/PCbuild/ssl.vcxproj @@ -122,7 +122,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> @@ -133,7 +133,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> @@ -144,7 +144,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> @@ -155,7 +155,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='Release|x64'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> @@ -166,7 +166,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='PGInstrument|Win32'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGInstrument|Win32'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGInstrument|Win32'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='PGInstrument|Win32'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='PGInstrument|Win32'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='PGInstrument|Win32'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> @@ -177,7 +177,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='PGInstrument|x64'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGInstrument|x64'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGInstrument|x64'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='PGInstrument|x64'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='PGInstrument|x64'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='PGInstrument|x64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> @@ -188,7 +188,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='PGUpdate|Win32'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGUpdate|Win32'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGUpdate|Win32'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='PGUpdate|Win32'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='PGUpdate|Win32'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='PGUpdate|Win32'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> @@ -199,7 +199,7 @@ "$(PythonExe)" build_ssl.py Release $(Platform) -a </NMakeBuildCommandLine> <NMakeReBuildCommandLine Condition="'$(Configuration)|$(Platform)'=='PGUpdate|x64'" /> - <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGUpdate|x64'" /> + <NMakeCleanCommandLine Condition="'$(Configuration)|$(Platform)'=='PGUpdate|x64'">echo OpenSSL must be cleaned manually if you want to rebuild it.</NMakeCleanCommandLine> <NMakeOutput Condition="'$(Configuration)|$(Platform)'=='PGUpdate|x64'" /> <NMakePreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='PGUpdate|x64'">$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions> <NMakeIncludeSearchPath Condition="'$(Configuration)|$(Platform)'=='PGUpdate|x64'">$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath> diff --git a/PCbuild/x64.props b/PCbuild/x64.props index 289d95b191..985c0ef7f7 100644 --- a/PCbuild/x64.props +++ b/PCbuild/x64.props @@ -5,7 +5,7 @@ </PropertyGroup> <ItemDefinitionGroup> <ClCompile> - <AdditionalOptions>/USECL:MS_OPTERON /GS- %(AdditionalOptions)</AdditionalOptions> + <BufferSecurityCheck>false</BufferSecurityCheck> <PreprocessorDefinitions>_WIN64;_M_X64;%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> diff --git a/Python/ceval.c b/Python/ceval.c index faee5cd44d..73925dc413 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2573,7 +2573,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) TARGET(WITH_CLEANUP) { - /* At the top of the stack are 1-3 values indicating + /* At the top of the stack are 1-6 values indicating how/why we entered the finally clause: - TOP = None - (TOP, SECOND) = (WHY_{RETURN,CONTINUE}), retval @@ -2586,9 +2586,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) otherwise we must call EXIT(None, None, None) - In the first two cases, we remove EXIT from the + In the first three cases, we remove EXIT from the stack, leaving the rest in the same order. In the - third case, we shift the bottom 3 values of the + fourth case, we shift the bottom 3 values of the stack down, and replace the empty spot with NULL. In addition, if the stack represents an exception, diff --git a/Python/fileutils.c b/Python/fileutils.c index b7c42e8e85..dbcb9234e0 100644 --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -623,7 +623,7 @@ int _Py_wreadlink(const wchar_t *path, wchar_t *buf, size_t bufsiz) { char *cpath; - char cbuf[PATH_MAX]; + char cbuf[MAXPATHLEN]; wchar_t *wbuf; int res; size_t r1; @@ -633,11 +633,11 @@ _Py_wreadlink(const wchar_t *path, wchar_t *buf, size_t bufsiz) errno = EINVAL; return -1; } - res = (int)readlink(cpath, cbuf, PATH_MAX); + res = (int)readlink(cpath, cbuf, Py_ARRAY_LENGTH(cbuf)); PyMem_Free(cpath); if (res == -1) return -1; - if (res == PATH_MAX) { + if (res == Py_ARRAY_LENGTH(cbuf)) { errno = EINVAL; return -1; } @@ -669,7 +669,7 @@ _Py_wrealpath(const wchar_t *path, wchar_t *resolved_path, size_t resolved_path_size) { char *cpath; - char cresolved_path[PATH_MAX]; + char cresolved_path[MAXPATHLEN]; wchar_t *wresolved_path; char *res; size_t r; @@ -709,11 +709,11 @@ _Py_wgetcwd(wchar_t *buf, size_t size) #ifdef MS_WINDOWS return _wgetcwd(buf, size); #else - char fname[PATH_MAX]; + char fname[MAXPATHLEN]; wchar_t *wname; size_t len; - if (getcwd(fname, PATH_MAX) == NULL) + if (getcwd(fname, Py_ARRAY_LENGTH(fname)) == NULL) return NULL; wname = _Py_char2wchar(fname, &len); if (wname == NULL) diff --git a/Python/getcopyright.c b/Python/getcopyright.c index 2d26787dfb..2b19622d43 100644 --- a/Python/getcopyright.c +++ b/Python/getcopyright.c @@ -2,7 +2,7 @@ #include "Python.h" -static char cprt[] = +static const char cprt[] = "\ Copyright (c) 2001-2013 Python Software Foundation.\n\ All Rights Reserved.\n\ diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 832df535f8..e02dbe2be1 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -817,8 +817,9 @@ Py_GetPythonHome(void) if (home == NULL && !Py_IgnoreEnvironmentFlag) { char* chome = Py_GETENV("PYTHONHOME"); if (chome) { - size_t r = mbstowcs(env_home, chome, PATH_MAX+1); - if (r != (size_t)-1 && r <= PATH_MAX) + size_t size = Py_ARRAY_LENGTH(env_home); + size_t r = mbstowcs(env_home, chome, size); + if (r != (size_t)-1 && r < size) home = env_home; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 1a41d2fa71..df9dfb1f0a 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1895,7 +1895,7 @@ sys_update_path(int argc, wchar_t **argv) #else /* All other filename syntaxes */ if (_HAVE_SCRIPT_ARGUMENT(argc, argv)) { #if defined(HAVE_REALPATH) - if (_Py_wrealpath(argv0, fullpath, PATH_MAX)) { + if (_Py_wrealpath(argv0, fullpath, Py_ARRAY_LENGTH(fullpath))) { argv0 = fullpath; } #endif |
