From: Hyunjee Kim Date: Mon, 25 Nov 2019 08:02:02 +0000 (+0900) Subject: Imported Upstream version 2.7.17 X-Git-Tag: upstream/2.7.17^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=22d1742a72b0ea5e939dfe68a5dc521ff514f2c0;p=platform%2Fupstream%2Fpython.git Imported Upstream version 2.7.17 --- diff --git a/Doc/Makefile b/Doc/Makefile index ebabc02..3d7e4ac 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -5,7 +5,9 @@ # You can set these variables from the command line. PYTHON = python -SPHINXBUILD = sphinx-build +VENVDIR = ./venv +SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build +BLURB = PATH=$(VENVDIR)/bin:$$PATH blurb PAPER = SOURCES = DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py) @@ -102,7 +104,12 @@ htmlview: html $(PYTHON) -c "import webbrowser; webbrowser.open('build/html/index.html')" clean: - -rm -rf build/* + -rm -rf build/* $(VENVDIR)/* + +venv: + $(PYTHON) -m venv $(VENVDIR) + $(VENVDIR)/bin/python3 -m pip install -U Sphinx blurb + @echo "The venv has been created in the $(VENVDIR) directory" dist: rm -rf dist @@ -148,7 +155,7 @@ dist: cp -pPR build/epub/Python.epub dist/python-$(DISTVERSION)-docs.epub check: - $(PYTHON) tools/rstlint.py -i tools + $(PYTHON)2 tools/rstlint.py -i tools -i $(VENVDIR) serve: ../Tools/scripts/serve.py build/html diff --git a/Doc/c-api/exceptions.rst b/Doc/c-api/exceptions.rst index db302d8..281e4c8 100644 --- a/Doc/c-api/exceptions.rst +++ b/Doc/c-api/exceptions.rst @@ -44,8 +44,12 @@ is a separate error indicator for each thread. .. c:function:: void PyErr_PrintEx(int set_sys_last_vars) Print a standard traceback to ``sys.stderr`` and clear the error indicator. - Call this function only when the error indicator is set. (Otherwise it will - cause a fatal error!) + **Unless** the error is a ``SystemExit``. In that case the no traceback + is printed and Python process will exit with the error code specified by + the ``SystemExit`` instance. + + Call this function **only** when the error indicator is set. Otherwise it + will cause a fatal error! If *set_sys_last_vars* is nonzero, the variables :data:`sys.last_type`, :data:`sys.last_value` and :data:`sys.last_traceback` will be set to the diff --git a/Doc/c-api/list.rst b/Doc/c-api/list.rst index 0aed0f3..a5e4a45 100644 --- a/Doc/c-api/list.rst +++ b/Doc/c-api/list.rst @@ -76,9 +76,9 @@ List Objects .. c:function:: PyObject* PyList_GetItem(PyObject *list, Py_ssize_t index) Return the object at position *index* in the list pointed to by *list*. The - position must be positive, indexing from the end of the list is not - supported. If *index* is out of bounds, return *NULL* and set an - :exc:`IndexError` exception. + position must be non-negative; indexing from the end of the list is not + supported. If *index* is out of bounds (<0 or >=len(list)), + return *NULL* and set an :exc:`IndexError` exception. .. versionchanged:: 2.5 This function used an :c:type:`int` for *index*. This might require diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 28fb589..4684d1b 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -217,6 +217,9 @@ Long Integer Objects Return a C :c:type:`unsigned long` from a Python long integer, without checking for overflow. + Returns ``(unsigned long)-1`` on error. Use :c:func:`PyErr_Occurred` to + disambiguate. + .. versionadded:: 2.3 @@ -225,6 +228,9 @@ Long Integer Objects Return a C :c:type:`unsigned long long` from a Python long integer, without checking for overflow. + Returns ``(unsigned PY_LONG_LONG)-1`` on error. Use + :c:func:`PyErr_Occurred` to disambiguate. + .. versionadded:: 2.3 diff --git a/Doc/conf.py b/Doc/conf.py index 557fe1e..393018d 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -41,6 +41,8 @@ exclude_patterns = [ # Require Sphinx 1.2 for build. needs_sphinx = '1.2' +# Avoid a warning with Sphinx >= 2.0 +master_doc = 'contents' # Options for HTML output # ----------------------- @@ -57,7 +59,7 @@ templates_path = ['tools/templates'] # Custom sidebar templates, filenames relative to this file. html_sidebars = { - 'index': 'indexsidebar.html', + 'index': ['indexsidebar.html'], } # Additional templates that should be rendered to pages. @@ -82,11 +84,10 @@ html_split_index = True # Options for LaTeX output # ------------------------ +latex_engine = 'xelatex' + # Get LaTeX to handle Unicode correctly latex_elements = { - 'inputenc': r'\usepackage[utf8x]{inputenc}', - 'utf8extra': '', - 'fontenc': r'\usepackage[T1,T2A]{fontenc}', } # Additional stuff for the LaTeX preamble. diff --git a/Doc/distutils/packageindex.rst b/Doc/distutils/packageindex.rst index cd11f20..f74c439 100644 --- a/Doc/distutils/packageindex.rst +++ b/Doc/distutils/packageindex.rst @@ -8,244 +8,10 @@ The Python Package Index (PyPI) ******************************* -The `Python Package Index (PyPI)`_ stores :ref:`meta-data ` -describing distributions packaged with distutils, as well as package data like -distribution files if a package author wishes. - -Distutils provides the :command:`register` and :command:`upload` commands for -pushing meta-data and distribution files to PyPI, respectively. See -:ref:`package-commands` for information on these commands. - - -PyPI overview -============= - -PyPI lets you submit any number of versions of your distribution to the index. -If you alter the meta-data for a particular version, you can submit it again -and the index will be updated. - -PyPI holds a record for each (name, version) combination submitted. The first -user to submit information for a given name is designated the Owner of that -name. Changes can be submitted through the :command:`register` command or -through the web interface. Owners can designate other users as Owners or -Maintainers. Maintainers can edit the package information, but not designate -new Owners or Maintainers. - -By default PyPI displays only the newest version of a given package. The web -interface lets one change this default behavior and manually select which -versions to display and hide. - -For each version, PyPI displays a home page. The home page is created from -the ``long_description`` which can be submitted via the :command:`register` -command. See :ref:`package-display` for more information. - - -.. _package-commands: - -Distutils commands -================== - -Distutils exposes two commands for submitting package data to PyPI: the -:ref:`register ` command for submitting meta-data to PyPI -and the :ref:`upload ` command for submitting distribution -files. Both commands read configuration data from a special file called a -:ref:`.pypirc file `. - - -.. _package-register: - -The ``register`` command ------------------------- - -The distutils command :command:`register` is used to submit your distribution's -meta-data to an index server. It is invoked as follows:: - - python setup.py register - -Distutils will respond with the following prompt:: - - running register - We need to know who you are, so please choose either: - 1. use your existing login, - 2. register as a new user, - 3. have the server generate a new password for you (and email it to you), or - 4. quit - Your selection [default 1]: - -Note: if your username and password are saved locally, you will not see this -menu. Also, refer to :ref:`pypirc` for how to store your credentials in a -:file:`.pypirc` file. - -If you have not registered with PyPI, then you will need to do so now. You -should choose option 2, and enter your details as required. Soon after -submitting your details, you will receive an email which will be used to confirm -your registration. - -Once you are registered, you may choose option 1 from the menu. You will be -prompted for your PyPI username and password, and :command:`register` will then -submit your meta-data to the index. - -See :ref:`package-cmdoptions` for options to the :command:`register` command. - - -.. _package-upload: - -The ``upload`` command ----------------------- - -.. versionadded:: 2.5 - -The distutils command :command:`upload` pushes the distribution files to PyPI. - -The command is invoked immediately after building one or more distribution -files. For example, the command :: - - python setup.py sdist bdist_wininst upload - -will cause the source distribution and the Windows installer to be uploaded to -PyPI. Note that these will be uploaded even if they are built using an earlier -invocation of :file:`setup.py`, but that only distributions named on the command -line for the invocation including the :command:`upload` command are uploaded. - -If a :command:`register` command was previously called in the same command, -and if the password was entered in the prompt, :command:`upload` will reuse the -entered password. This is useful if you do not want to store a password in -clear text in a :file:`.pypirc` file. - -You can use the ``--sign`` option to tell :command:`upload` to sign each -uploaded file using GPG (GNU Privacy Guard). The :program:`gpg` program must -be available for execution on the system :envvar:`PATH`. You can also specify -which key to use for signing using the ``--identity=name`` option. - -See :ref:`package-cmdoptions` for additional options to the :command:`upload` -command. - - -.. _package-cmdoptions: - -Additional command options --------------------------- - -This section describes options common to both the :command:`register` and -:command:`upload` commands. - -The ``--repository`` or ``-r`` option lets you specify a PyPI server -different from the default. For example:: - - python setup.py sdist bdist_wininst upload -r https://example.com/pypi - -For convenience, a name can be used in place of the URL when the -:file:`.pypirc` file is configured to do so. For example:: - - python setup.py register -r other - -See :ref:`pypirc` for more information on defining alternate servers. - -The ``--show-response`` option displays the full response text from the PyPI -server, which is useful when debugging problems with registering and uploading. - - -.. index:: - single: .pypirc file - single: Python Package Index (PyPI); .pypirc file - -.. _pypirc: - -The ``.pypirc`` file --------------------- - -The :command:`register` and :command:`upload` commands both check for the -existence of a :file:`.pypirc` file at the location :file:`$HOME/.pypirc`. -If this file exists, the command uses the username, password, and repository -URL configured in the file. The format of a :file:`.pypirc` file is as -follows:: - - [distutils] - index-servers = - pypi - - [pypi] - repository: - username: - password: - -The *distutils* section defines an *index-servers* variable that lists the -name of all sections describing a repository. - -Each section describing a repository defines three variables: - -- *repository*, that defines the url of the PyPI server. Defaults to - ``https://www.python.org/pypi``. -- *username*, which is the registered username on the PyPI server. -- *password*, that will be used to authenticate. If omitted the user - will be prompt to type it when needed. - -If you want to define another server a new section can be created and -listed in the *index-servers* variable:: - - [distutils] - index-servers = - pypi - other - - [pypi] - repository: - username: - password: - - [other] - repository: https://example.com/pypi - username: - password: - -This allows the :command:`register` and :command:`upload` commands to be -called with the ``--repository`` option as described in -:ref:`package-cmdoptions`. - -Specifically, you might want to add the `PyPI Test Repository -`_ to your ``.pypirc`` to facilitate -testing before doing your first upload to ``PyPI`` itself. - - -.. _package-display: - -PyPI package display -==================== - -The ``long_description`` field plays a special role at PyPI. It is used by -the server to display a home page for the registered package. - -If you use the `reStructuredText `_ -syntax for this field, PyPI will parse it and display an HTML output for -the package home page. - -The ``long_description`` field can be attached to a text file located -in the package:: - - from distutils.core import setup - - with open('README.txt') as file: - long_description = file.read() - - setup(name='Distutils', - long_description=long_description) - -In that case, :file:`README.txt` is a regular reStructuredText text file located -in the root of the package besides :file:`setup.py`. - -To prevent registering broken reStructuredText content, you can use the -:program:`rst2html` program that is provided by the :mod:`docutils` package and -check the ``long_description`` from the command line: - -.. code-block:: shell-session - - $ python setup.py --long-description | rst2html.py > output.html - -:mod:`docutils` will display a warning if there's something wrong with your -syntax. Because PyPI applies additional checks (e.g. by passing ``--no-raw`` -to ``rst2html.py`` in the command above), being able to run the command above -without warnings does not guarantee that PyPI will convert the content -successfully. +The `Python Package Index (PyPI)`_ stores metadata describing distributions +packaged with distutils and other publishing tools, as well the distribution +archives themselves. +Detailed instructions on using PyPI at :ref:`distributing-index`. .. _Python Package Index (PyPI): https://pypi.org diff --git a/Doc/distutils/setupscript.rst b/Doc/distutils/setupscript.rst index 8407206..fae570f 100644 --- a/Doc/distutils/setupscript.rst +++ b/Doc/distutils/setupscript.rst @@ -612,9 +612,8 @@ Notes: `_. (5) - The ``long_description`` field is used by PyPI when you are - :ref:`registering ` a package, to - :ref:`build its home page `. + The ``long_description`` field is used by PyPI when you publish a package, + to build its project page. (6) The ``license`` field is a text indicating the license covering the diff --git a/Doc/faq/general.rst b/Doc/faq/general.rst index 887dea7..27f4681 100644 --- a/Doc/faq/general.rst +++ b/Doc/faq/general.rst @@ -268,14 +268,8 @@ Python references; or perhaps search for "Python" and "language". Where in the world is www.python.org located? --------------------------------------------- -The Python project's infrastructure is located all over the world. -`www.python.org `_ is graciously hosted by `Rackspace -`_, with CDN caching provided by `Fastly -`_. `Upfront Systems -`_ hosts `bugs.python.org -`_. Many other Python services like `the Wiki -`_ are hosted by `Oregon State -University Open Source Lab `_. +The Python project's infrastructure is located all over the world and is managed +by the Python Infrastructure Team. Details `here `__. Why is it called Python? @@ -312,14 +306,10 @@ guaranteed that interfaces will remain the same throughout a series of bugfix releases. The latest stable releases can always be found on the `Python download page -`_. There are two production-ready version -of Python: 2.x and 3.x, but the recommended one at this times is Python 3.x. -Although Python 2.x is still widely used, `it will not be -maintained after January 1, 2020 `_. -Python 2.x was known for having more third-party libraries available, however, -by the time of this writing, most of the widely used libraries support Python 3.x, -and some are even dropping the Python 2.x support. - +`_. There are two production-ready versions +of Python: 2.x and 3.x. The recommended version is 3.x, which is supported by +most widely used libraries. Although 2.x is still widely used, `it will not +be maintained after January 1, 2020 `_. How many people are using Python? --------------------------------- diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst index 9190e1a..62b34b6 100644 --- a/Doc/faq/programming.rst +++ b/Doc/faq/programming.rst @@ -997,9 +997,27 @@ To convert, e.g., the number 144 to the string '144', use the built-in type constructor :func:`str`. If you want a hexadecimal or octal representation, use the built-in functions :func:`hex` or :func:`oct`. For fancy formatting, see the :ref:`formatstrings` section, e.g. ``"{:04d}".format(144)`` yields -``'0144'`` and ``"{:.3f}".format(1/3)`` yields ``'0.333'``. You may also use -:ref:`the % operator ` on strings. See the library reference -manual for details. +``'0144'`` and ``"{:.3f}".format(1.0/3.0)`` yields ``'0.333'``. In Python 2, the +division (/) operator returns the floor of the mathematical result of division +if the arguments are ints or longs, but it returns a reasonable approximation of +the division result if the arguments are floats or complex:: + + >>> print('{:.3f}'.format(1/3)) + 0.000 + >>> print('{:.3f}'.format(1.0/3)) + 0.333 + +In Python 3, the default behaviour of the division operator (see :pep:`238`) has +been changed but you can have the same behaviour in Python 2 if you import +``division`` from :mod:`__future__`:: + + >>> from __future__ import division + >>> print('{:.3f}'.format(1/3)) + 0.333 + + +You may also use :ref:`the % operator ` on strings. See the +library reference manual for details. How do I modify a string in place? diff --git a/Doc/glossary.rst b/Doc/glossary.rst index 9e6bf23..43abf50 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -490,6 +490,11 @@ Glossary :meth:`load_module`. A loader is typically returned by a :term:`finder`. See :pep:`302` for details. + magic method + .. index:: pair: magic; method + + An informal synonym for :term:`special method`. + mapping A container object that supports arbitrary key lookups and implements the methods specified in the :class:`~collections.Mapping` or @@ -698,6 +703,8 @@ Glossary versions, :meth:`__getslice__` and :meth:`__setslice__`). special method + .. index:: pair: special; method + A method that is called implicitly by Python to execute a certain operation on a type, such as addition. Such methods have names starting and ending with double underscores. Special methods are documented in diff --git a/Doc/howto/regex.rst b/Doc/howto/regex.rst index 082fc01..81c0495 100644 --- a/Doc/howto/regex.rst +++ b/Doc/howto/regex.rst @@ -101,8 +101,9 @@ special nature. You can match the characters not listed within the class by :dfn:`complementing` the set. This is indicated by including a ``'^'`` as the first character of the -class; ``'^'`` outside a character class will simply match the ``'^'`` -character. For example, ``[^5]`` will match any character except ``'5'``. +class. For example, ``[^5]`` will match any character except ``'5'``. If the +caret appears elsewhere in a character class, it does not have special meaning. +For example: ``[5^]`` will match either a ``'5'`` or a ``'^'``. Perhaps the most important metacharacter is the backslash, ``\``. As in Python string literals, the backslash can be followed by various characters to signal diff --git a/Doc/library/_winreg.rst b/Doc/library/_winreg.rst index a87cb9c..177e199 100644 --- a/Doc/library/_winreg.rst +++ b/Doc/library/_winreg.rst @@ -427,7 +427,7 @@ This module offers the following functions: *key* is an already open key, or one of the predefined :ref:`HKEY_* constants `. - Will generally raise :exc:`NotImplemented` if executed on a 32-bit + Will generally raise :exc:`NotImplementedError` if executed on a 32-bit operating system. If the key is not on the reflection list, the function succeeds but has no @@ -442,7 +442,7 @@ This module offers the following functions: *key* is an already open key, or one of the predefined :ref:`HKEY_* constants `. - Will generally raise :exc:`NotImplemented` if executed on a 32-bit + Will generally raise :exc:`NotImplementedError` if executed on a 32-bit operating system. Restoring reflection for a key does not affect reflection of any subkeys. @@ -457,7 +457,7 @@ This module offers the following functions: Returns ``True`` if reflection is disabled. - Will generally raise :exc:`NotImplemented` if executed on a 32-bit + Will generally raise :exc:`NotImplementedError` if executed on a 32-bit operating system. diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 6d5855b..b04a92d 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -129,6 +129,11 @@ and classes for traversing abstract syntax trees: Parse the source into an AST node. Equivalent to ``compile(source, filename, mode, ast.PyCF_ONLY_AST)``. + .. warning:: + It is possible to crash the Python interpreter with a + sufficiently large/complex string due to stack depth limitations + in Python's AST compiler. + .. function:: literal_eval(node_or_string) @@ -142,6 +147,11 @@ and classes for traversing abstract syntax trees: capable of evaluating arbitrarily complex expressions, for example involving operators or indexing. + .. warning:: + It is possible to crash the Python interpreter with a + sufficiently large/complex string due to stack depth limitations + in Python's AST compiler. + .. function:: get_docstring(node, clean=True) diff --git a/Doc/library/colorsys.rst b/Doc/library/colorsys.rst index f1447e8..32eac8e 100644 --- a/Doc/library/colorsys.rst +++ b/Doc/library/colorsys.rst @@ -20,7 +20,7 @@ spaces, the coordinates are all between 0 and 1. .. seealso:: More information about color spaces can be found at - http://www.poynton.com/ColorFAQ.html and + https://www.poynton.com/ColorFAQ.html and https://www.cambridgeincolour.com/tutorials/color-spaces.htm. The :mod:`colorsys` module defines the following functions: diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index df9ccbf..6a52991 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -565,7 +565,7 @@ Here is a simple example of a POINT structure, which contains two integers named >>> POINT(1, 2, 3) Traceback (most recent call last): File "", line 1, in - ValueError: too many initializers + TypeError: too many initializers >>> You can, however, build much more complicated structures. A structure can diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst index 8757c6c..3cb9445 100644 --- a/Doc/library/exceptions.rst +++ b/Doc/library/exceptions.rst @@ -524,6 +524,13 @@ module for more information. .. versionadded:: 2.5 +.. exception:: BytesWarning + + Base class for warnings related to bytes and bytearray. + + .. versionadded:: 2.6 + + Exception hierarchy ------------------- diff --git a/Doc/library/functions.rst b/Doc/library/functions.rst index 4386e60..23f34a3 100644 --- a/Doc/library/functions.rst +++ b/Doc/library/functions.rst @@ -175,19 +175,18 @@ section. def f(cls, arg1, arg2, ...): ... - The ``@classmethod`` form is a function :term:`decorator` -- see the description - of function definitions in :ref:`function` for details. + The ``@classmethod`` form is a function :term:`decorator` -- see + :ref:`function` for details. - It can be called either on the class (such as ``C.f()``) or on an instance (such + A class method can be called either on the class (such as ``C.f()``) or on an instance (such as ``C().f()``). The instance is ignored except for its class. If a class method is called for a derived class, the derived class object is passed as the implied first argument. Class methods are different than C++ or Java static methods. If you want those, - see :func:`staticmethod` in this section. + see :func:`staticmethod`. - For more information on class methods, consult the documentation on the standard - type hierarchy in :ref:`types`. + For more information on class methods, see :ref:`types`. .. versionadded:: 2.2 @@ -249,6 +248,12 @@ section. character. This is to facilitate detection of incomplete and complete statements in the :mod:`code` module. + .. warning:: + + It is possible to crash the Python interpreter with a + sufficiently large/complex string when compiling to an AST + object due to stack depth limitations in Python's AST compiler. + .. versionchanged:: 2.3 The *flags* and *dont_inherit* arguments were added. @@ -1346,18 +1351,17 @@ section. def f(arg1, arg2, ...): ... - The ``@staticmethod`` form is a function :term:`decorator` -- see the - description of function definitions in :ref:`function` for details. + The ``@staticmethod`` form is a function :term:`decorator` -- see + :ref:`function` for details. - It can be called either on the class (such as ``C.f()``) or on an instance (such - as ``C().f()``). The instance is ignored except for its class. + A static method can be called either on the class (such as ``C.f()``) or on an instance (such + as ``C().f()``). Static methods in Python are similar to those found in Java or C++. Also see :func:`classmethod` for a variant that is useful for creating alternate class constructors. - For more information on static methods, consult the documentation on the - standard type hierarchy in :ref:`types`. + For more information on static methods, see :ref:`types`. .. versionadded:: 2.2 diff --git a/Doc/library/io.rst b/Doc/library/io.rst index dcdd01c..7c053f9 100644 --- a/Doc/library/io.rst +++ b/Doc/library/io.rst @@ -304,7 +304,7 @@ I/O Base Classes Note that it's already possible to iterate on file objects using ``for line in file: ...`` without calling ``file.readlines()``. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given byte *offset*. *offset* is interpreted relative to the position indicated by *whence*. The default @@ -736,7 +736,7 @@ Text I/O If *limit* is specified, at most *limit* characters will be read. - .. method:: seek(offset[, whence]) + .. method:: seek(offset, whence=SEEK_SET) Change the stream position to the given *offset*. Behaviour depends on the *whence* parameter. The default value for *whence* is diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 17303dd..18477f1 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -659,6 +659,10 @@ loops that truncate the stream. used anywhere else; otherwise, the *iterable* could get advanced without the tee objects being informed. + ``tee`` iterators are not threadsafe. A :exc:`RuntimeError` may be + raised when using simultaneously iterators returned by the same :func:`tee` + call, even if the original *iterable* is threadsafe. + This itertool may require significant auxiliary storage (depending on how much temporary data needs to be stored). In general, if one iterator uses most or all of the data before another iterator starts, it is faster to use diff --git a/Doc/library/select.rst b/Doc/library/select.rst index 7a08045..b68b324 100644 --- a/Doc/library/select.rst +++ b/Doc/library/select.rst @@ -291,13 +291,14 @@ Kqueue Objects Create a kqueue object from a given file descriptor. -.. method:: kqueue.control(changelist, max_events[, timeout=None]) -> eventlist +.. method:: kqueue.control(changelist, max_events[, timeout]) -> eventlist Low level interface to kevent - - changelist must be an iterable of kevent object or ``None`` + - changelist must be an iterable of kevent objects or ``None`` - max_events must be 0 or a positive integer - - timeout in seconds (floats possible) + - timeout in seconds (floats possible); the default is ``None``, + to wait forever .. _kevent-objects: diff --git a/Doc/library/simplehttpserver.rst b/Doc/library/simplehttpserver.rst index df8699e..1eca9fe 100644 --- a/Doc/library/simplehttpserver.rst +++ b/Doc/library/simplehttpserver.rst @@ -13,7 +13,7 @@ .. warning:: - mod:`SimpleHTTServer` is not recommended for production. It only implements + :mod:`SimpleHTTPServer` is not recommended for production. It only implements basic security checks. The :mod:`SimpleHTTPServer` module defines a single class, diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index ff68738..b4fe19a 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1909,7 +1909,7 @@ The constructors for both classes work the same: .. method:: copy() - Return a new set with a shallow copy of *s*. + Return a shallow copy of the set. Note, the non-operator versions of :meth:`union`, :meth:`intersection`, diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 5a7647b..339625a 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -1030,7 +1030,7 @@ always available. .. note:: Python is now `developed `_ using - Mercurial. In recent Python 2.7 bugfix releases, :data:`subversion` + Git. In recent Python 2.7 bugfix releases, :data:`subversion` therefore contains placeholder information. It is removed in Python 3.3. diff --git a/Doc/library/telnetlib.rst b/Doc/library/telnetlib.rst index a3019f5..73721f5 100644 --- a/Doc/library/telnetlib.rst +++ b/Doc/library/telnetlib.rst @@ -29,7 +29,7 @@ Character), EL (Erase Line), GA (Go Ahead), SB (Subnegotiation Begin). .. class:: Telnet([host[, port[, timeout]]]) :class:`Telnet` represents a connection to a Telnet server. The instance is - initially not connected by default; the :meth:`open` method must be used to + initially not connected by default; the :meth:`~Telnet.open` method must be used to establish a connection. Alternatively, the host name and optional port number can be passed to the constructor, to, in which case the connection to the server will be established before the constructor returns. The optional diff --git a/Doc/library/test.rst b/Doc/library/test.rst index eef5d16..9d78c90 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -246,6 +246,11 @@ The :mod:`test.support` module defines the following constants: Set to a name that is safe to use as the name of a temporary file. Any temporary file that is created should be closed and unlinked (removed). + +.. data:: TEST_HTTP_URL + + Define the URL of a dedicated HTTP server for the network tests. + The :mod:`test.support` module defines the following functions: diff --git a/Doc/library/textwrap.rst b/Doc/library/textwrap.rst index a50600e..6b0decb 100644 --- a/Doc/library/textwrap.rst +++ b/Doc/library/textwrap.rst @@ -66,6 +66,9 @@ indentation from strings that have unwanted whitespace to the left of the text. of this module incorrectly expanded tabs before searching for common leading whitespace.) + Lines containing only whitespace are ignored in the input and normalized to a + single newline character in the output. + For example:: def test(): diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index 3356e4e..9aa6dcb 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -33,7 +33,7 @@ installed, so you can read the Tcl/Tk documentation specific to that version. `TKDocs `_ Extensive tutorial plus friendlier widget pages for some of the widgets. - `Tkinter reference: a GUI for Python `_ + `Tkinter 8.5 reference: a GUI for Python `_ On-line reference material. `Tkinter docs from effbot `_ @@ -43,7 +43,7 @@ installed, so you can read the Tcl/Tk documentation specific to that version. Book by Mark Lutz, has excellent coverage of Tkinter. `Modern Tkinter for Busy Python Developers `_ - Book by Mark Rozerman about building attractive and modern graphical user interfaces with Python and Tkinter. + Book by Mark Roseman about building attractive and modern graphical user interfaces with Python and Tkinter. `Python and Tkinter Programming `_ Book by John Grayson (ISBN 1-884777-81-3). diff --git a/Doc/library/unicodedata.rst b/Doc/library/unicodedata.rst index d7c48c4..bed35ef 100644 --- a/Doc/library/unicodedata.rst +++ b/Doc/library/unicodedata.rst @@ -20,7 +20,7 @@ based on the :file:`UnicodeData.txt` file version 5.2.0 which is publicly available from ftp://ftp.unicode.org/. The module uses the same names and symbols as defined by the UnicodeData File -Format 5.2.0 (see http://www.unicode.org/reports/tr44/tr44-4.html). +Format 5.2.0 (see https://www.unicode.org/reports/tr44/). It defines the following functions: diff --git a/Doc/library/urlparse.rst b/Doc/library/urlparse.rst index 22249da..0989c88 100644 --- a/Doc/library/urlparse.rst +++ b/Doc/library/urlparse.rst @@ -119,12 +119,22 @@ The :mod:`urlparse` module defines the following functions: See section :ref:`urlparse-result-object` for more information on the result object. + Characters in the :attr:`netloc` attribute that decompose under NFKC + normalization (as used by the IDNA encoding) into any of ``/``, ``?``, + ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is + decomposed before parsing, or is not a Unicode string, no error will be + raised. + .. versionchanged:: 2.5 Added attributes to return value. .. versionchanged:: 2.7 Added IPv6 URL parsing capabilities. + .. versionchanged:: 2.7.17 + Characters that affect netloc parsing under NFKC normalization will + now raise :exc:`ValueError`. + .. function:: parse_qs(qs[, keep_blank_values[, strict_parsing[, max_num_fields]]]) @@ -232,11 +242,21 @@ The :mod:`urlparse` module defines the following functions: See section :ref:`urlparse-result-object` for more information on the result object. + Characters in the :attr:`netloc` attribute that decompose under NFKC + normalization (as used by the IDNA encoding) into any of ``/``, ``?``, + ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is + decomposed before parsing, or is not a Unicode string, no error will be + raised. + .. versionadded:: 2.2 .. versionchanged:: 2.5 Added attributes to return value. + .. versionchanged:: 2.7.17 + Characters that affect netloc parsing under NFKC normalization will + now raise :exc:`ValueError`. + .. function:: urlunsplit(parts) diff --git a/Doc/library/warnings.rst b/Doc/library/warnings.rst index e82bb97..2f699ea 100644 --- a/Doc/library/warnings.rst +++ b/Doc/library/warnings.rst @@ -91,6 +91,9 @@ following warnings category classes are currently defined: | :exc:`UnicodeWarning` | Base category for warnings related to | | | Unicode. | +----------------------------------+-----------------------------------------------+ +| :exc:`BytesWarning` | Base category for warnings related to | +| | bytes and bytearray. | ++----------------------------------+-----------------------------------------------+ While these are technically built-in exceptions, they are documented here, because conceptually they belong to the warnings mechanism. diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py index 6378f76..1ec88c2 100644 --- a/Doc/tools/extensions/pyspecific.py +++ b/Doc/tools/extensions/pyspecific.py @@ -15,7 +15,6 @@ SOURCE_URI = 'https://github.com/python/cpython/tree/2.7/%s' from docutils import nodes, utils from docutils.parsers.rst import Directive -from sphinx.util import status_iterator from sphinx.util.nodes import split_explicit_title from sphinx.writers.html import HTMLTranslator from sphinx.writers.latex import LaTeXTranslator @@ -173,6 +172,11 @@ class PydocTopicsBuilder(Builder): return '' # no URIs def write(self, *ignored): + try: # sphinx>=1.6 + from sphinx.util import status_iterator + except ImportError: # sphinx<1.6 + status_iterator = self.status_iterator + writer = TextWriter(self) for label in status_iterator(pydoc_topic_labels, 'building topics... ', diff --git a/Doc/tools/extensions/suspicious.py b/Doc/tools/extensions/suspicious.py index 0a70e57..494efab 100644 --- a/Doc/tools/extensions/suspicious.py +++ b/Doc/tools/extensions/suspicious.py @@ -48,6 +48,7 @@ import sys from docutils import nodes from sphinx.builders import Builder +import sphinx.util detect_all = re.compile(r''' ::(?=[^=])| # two :: (but NOT ::=) @@ -85,6 +86,7 @@ class CheckSuspiciousMarkupBuilder(Builder): Checks for possibly invalid markup that may leak into the output. """ name = 'suspicious' + logger = sphinx.util.logging.getLogger("CheckSuspiciousMarkupBuilder") def init(self): # create output file @@ -116,7 +118,7 @@ class CheckSuspiciousMarkupBuilder(Builder): self.warn('Found %s/%s unused rules:' % (len(unused_rules), len(self.rules))) for rule in unused_rules: - self.info(repr(rule)) + self.logger.info(repr(rule)) return def check_issue(self, line, lineno, issue): @@ -146,7 +148,6 @@ class CheckSuspiciousMarkupBuilder(Builder): return False def report_issue(self, text, lineno, issue): - if not self.any_issue: self.info() self.any_issue = True self.write_log_entry(lineno, issue, text) if py3: @@ -181,7 +182,7 @@ class CheckSuspiciousMarkupBuilder(Builder): A csv file, with exactly the same format as suspicious.csv Fields: document name (normalized), line number, issue, surrounding text """ - self.info("loading ignore rules... ", nonl=1) + self.logger.info("loading ignore rules... ", nonl=1) self.rules = rules = [] try: if py3: @@ -206,7 +207,7 @@ class CheckSuspiciousMarkupBuilder(Builder): rule = Rule(docname, lineno, issue, text) rules.append(rule) f.close() - self.info('done, %d rules loaded' % len(self.rules)) + self.logger.info('done, %d rules loaded' % len(self.rules)) def get_lineno(node): diff --git a/Doc/tools/static/sidebar.js b/Doc/tools/static/sidebar.js index 1bdd829..17f818e 100644 --- a/Doc/tools/static/sidebar.js +++ b/Doc/tools/static/sidebar.js @@ -46,6 +46,15 @@ $(function() { var dark_color = $('.related').css('background-color'); var light_color = $('.document').css('background-color'); + // set position: sticky on sidebar + // (browsers that don't support this will fall-back to + // positioning via scroll_sidebar) + var supportsPositionSticky = (window.CSS && window.CSS.supports && + window.CSS.supports('position', 'sticky')); + if (supportsPositionSticky) { + sidebarwrapper.css('position', 'sticky'); + } + function get_viewport_height() { if (window.innerHeight) return window.innerHeight; @@ -157,6 +166,9 @@ $(function() { /* intelligent scrolling */ function scroll_sidebar() { + if (supportsPositionSticky) { + return; + } var sidebar_height = sidebarwrapper.height(); var viewport_height = get_viewport_height(); var offset = sidebar.position()['top']; diff --git a/Doc/tools/static/switchers.js b/Doc/tools/static/switchers.js index dbf907e..d4fc07d 100644 --- a/Doc/tools/static/switchers.js +++ b/Doc/tools/static/switchers.js @@ -10,7 +10,8 @@ '(?:release/\\d.\\d[\\x\\d\\.]*)']; var all_versions = { - '3.8': 'dev (3.8)', + '3.9': 'dev (3.9)', + '3.8': '3.8', '3.7': '3.7', '3.6': '3.6', '3.5': '3.5', diff --git a/Doc/tools/templates/indexsidebar.html b/Doc/tools/templates/indexsidebar.html index 1181f71..e5748f2 100644 --- a/Doc/tools/templates/indexsidebar.html +++ b/Doc/tools/templates/indexsidebar.html @@ -2,7 +2,8 @@

{% trans %}Download these documents{% endtrans %}

{% trans %}Docs by version{% endtrans %}

    -
  • {% trans %}Python 3.8 (in development){% endtrans %}
  • +
  • {% trans %}Python 3.9 (in development){% endtrans %}
  • +
  • {% trans %}Python 3.8 (stable){% endtrans %}
  • {% trans %}Python 3.7 (stable){% endtrans %}
  • {% trans %}Python 3.6 (security-fixes){% endtrans %}
  • {% trans %}Python 3.5 (security-fixes){% endtrans %}
  • diff --git a/Doc/tutorial/controlflow.rst b/Doc/tutorial/controlflow.rst index c1c3d28..2e1c122 100644 --- a/Doc/tutorial/controlflow.rst +++ b/Doc/tutorial/controlflow.rst @@ -4,8 +4,8 @@ More Control Flow Tools *********************** -Besides the :keyword:`while` statement just introduced, Python knows the usual -control flow statements known from other languages, with some twists. +Besides the :keyword:`while` statement just introduced, Python uses the usual +flow control statements known from other languages, with some twists. .. _tut-if: diff --git a/Doc/tutorial/modules.rst b/Doc/tutorial/modules.rst index 042d233..f767bb6 100644 --- a/Doc/tutorial/modules.rst +++ b/Doc/tutorial/modules.rst @@ -518,7 +518,7 @@ Although certain modules are designed to export only names that follow certain patterns when you use ``import *``, it is still considered bad practice in production code. -Remember, there is nothing wrong with using ``from Package import +Remember, there is nothing wrong with using ``from package import specific_submodule``! In fact, this is the recommended notation unless the importing module needs to use submodules with the same name from different packages. diff --git a/Include/code.h b/Include/code.h index 7456fd6..26c571a 100644 --- a/Include/code.h +++ b/Include/code.h @@ -104,7 +104,7 @@ PyAPI_FUNC(int) _PyCode_CheckLineNumber(PyCodeObject* co, * * Return (type(obj), obj, ...): a tuple with variable size (at least 2 items) * depending on the type and the value. The type is the first item to not - * compare bytes and str which can raise a BytesWarning exception. */ + * compare bytes and unicode which can raise a BytesWarning exception. */ PyAPI_FUNC(PyObject*) _PyCode_ConstantKey(PyObject *obj); PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts, diff --git a/Include/patchlevel.h b/Include/patchlevel.h index b12d901..4bfcdaf 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -22,12 +22,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 2 #define PY_MINOR_VERSION 7 -#define PY_MICRO_VERSION 16 +#define PY_MICRO_VERSION 17 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL #define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "2.7.16" +#define PY_VERSION "2.7.17" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository). Empty diff --git a/Include/pyport.h b/Include/pyport.h index 0c78a1e..35562a6 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -481,7 +481,7 @@ extern "C" { } \ } while(0) -/* Py_SET_ERANGE_ON_OVERFLOW(x) +/* Py_SET_ERANGE_IF_OVERFLOW(x) * An alias of Py_SET_ERRNO_ON_MATH_ERROR for backward-compatibility. */ #define Py_SET_ERANGE_IF_OVERFLOW(X) Py_SET_ERRNO_ON_MATH_ERROR(X) diff --git a/Lib/DocXMLRPCServer.py b/Lib/DocXMLRPCServer.py index 4064ec2..90b037d 100644 --- a/Lib/DocXMLRPCServer.py +++ b/Lib/DocXMLRPCServer.py @@ -20,6 +20,16 @@ from SimpleXMLRPCServer import (SimpleXMLRPCServer, CGIXMLRPCRequestHandler, resolve_dotted_attribute) + +def _html_escape_quote(s): + s = s.replace("&", "&") # Must be done first! + s = s.replace("<", "<") + s = s.replace(">", ">") + s = s.replace('"', """) + s = s.replace('\'', "'") + return s + + class ServerHTMLDoc(pydoc.HTMLDoc): """Class used to generate pydoc HTML document for a server""" @@ -210,7 +220,8 @@ class XMLRPCDocGenerator: methods ) - return documenter.page(self.server_title, documentation) + title = _html_escape_quote(self.server_title) + return documenter.page(title, documentation) class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): """XML-RPC and documentation request handler class. diff --git a/Lib/cookielib.py b/Lib/cookielib.py index 2dd7c48..1d56d3f 100644 --- a/Lib/cookielib.py +++ b/Lib/cookielib.py @@ -984,7 +984,7 @@ class DefaultCookiePolicy(CookiePolicy): req_path = request_path(request) if ((cookie.version > 0 or (cookie.version == 0 and self.strict_ns_set_path)) and - not req_path.startswith(cookie.path)): + not self.path_return_ok(cookie.path, request)): _debug(" path attribute %s is not a prefix of request " "path %s", cookie.path, req_path) return False @@ -1139,6 +1139,11 @@ class DefaultCookiePolicy(CookiePolicy): req_host, erhn = eff_request_host(request) domain = cookie.domain + if domain and not domain.startswith("."): + dotdomain = "." + domain + else: + dotdomain = domain + # strict check of non-domain cookies: Mozilla does this, MSIE5 doesn't if (cookie.version == 0 and (self.strict_ns_domain & self.DomainStrictNonDomain) and @@ -1151,7 +1156,7 @@ class DefaultCookiePolicy(CookiePolicy): _debug(" effective request-host name %s does not domain-match " "RFC 2965 cookie domain %s", erhn, domain) return False - if cookie.version == 0 and not ("."+erhn).endswith(domain): + if cookie.version == 0 and not ("."+erhn).endswith(dotdomain): _debug(" request-host %s does not match Netscape cookie domain " "%s", req_host, domain) return False @@ -1165,7 +1170,11 @@ class DefaultCookiePolicy(CookiePolicy): req_host = "."+req_host if not erhn.startswith("."): erhn = "."+erhn - if not (req_host.endswith(domain) or erhn.endswith(domain)): + if domain and not domain.startswith("."): + dotdomain = "." + domain + else: + dotdomain = domain + if not (req_host.endswith(dotdomain) or erhn.endswith(dotdomain)): #_debug(" request domain %s does not match cookie domain %s", # req_host, domain) return False @@ -1182,11 +1191,15 @@ class DefaultCookiePolicy(CookiePolicy): def path_return_ok(self, path, request): _debug("- checking cookie path=%s", path) req_path = request_path(request) - if not req_path.startswith(path): - _debug(" %s does not path-match %s", req_path, path) - return False - return True + pathlen = len(path) + if req_path == path: + return True + elif (req_path.startswith(path) and + (path.endswith("/") or req_path[pathlen:pathlen+1] == "/")): + return True + _debug(" %s does not path-match %s", req_path, path) + return False def vals_sorted_by_key(adict): keys = adict.keys() diff --git a/Lib/ctypes/test/test_arrays.py b/Lib/ctypes/test/test_arrays.py index 53859a3..ec00d9f 100644 --- a/Lib/ctypes/test/test_arrays.py +++ b/Lib/ctypes/test/test_arrays.py @@ -134,6 +134,27 @@ class ArrayTestCase(unittest.TestCase): t2 = my_int * 1 self.assertIs(t1, t2) + def test_empty_element_struct(self): + class EmptyStruct(Structure): + _fields_ = [] + + obj = (EmptyStruct * 2)() # bpo37188: Floating point exception + self.assertEqual(sizeof(obj), 0) + + def test_empty_element_array(self): + class EmptyArray(Array): + _type_ = c_int + _length_ = 0 + + obj = (EmptyArray * 2)() # bpo37188: Floating point exception + self.assertEqual(sizeof(obj), 0) + + def test_bpo36504_signed_int_overflow(self): + # The overflow check in PyCArrayType_new() could cause signed integer + # overflow. + with self.assertRaises(OverflowError): + c_char * sys.maxsize * 2 + @unittest.skipUnless(sys.maxsize > 2**32, 'requires 64bit platform') @precisionbigmemtest(size=_2G, memuse=1, dry_run=False) def test_large_array(self, size): diff --git a/Lib/ctypes/test/test_unicode.py b/Lib/ctypes/test/test_unicode.py index 1da5a25..ec5663a 100644 --- a/Lib/ctypes/test/test_unicode.py +++ b/Lib/ctypes/test/test_unicode.py @@ -93,7 +93,7 @@ class StringTestCase(UnicodeTestCase): func.argtypes = None func.restype = ctypes.c_int - def test_ascii_replace(self): + def test_ascii_strict(self): func = self.func ctypes.set_conversion_mode("ascii", "strict") self.assertEqual(func("abc"), "abc") diff --git a/Lib/distutils/ccompiler.py b/Lib/distutils/ccompiler.py index 88a910c..3a7b5b8 100644 --- a/Lib/distutils/ccompiler.py +++ b/Lib/distutils/ccompiler.py @@ -748,8 +748,9 @@ class CCompiler: for incl in includes: f.write("""#include "%s"\n""" % incl) f.write("""\ -main (int argc, char **argv) { +int main (int argc, char **argv) { %s(); + return 0; } """ % funcname) finally: diff --git a/Lib/distutils/command/build.py b/Lib/distutils/command/build.py index f84bf35..2360091 100644 --- a/Lib/distutils/command/build.py +++ b/Lib/distutils/command/build.py @@ -114,7 +114,7 @@ class build(Command): self.build_scripts = os.path.join(self.build_base, 'scripts-' + sys.version[0:3]) - if self.executable is None: + if self.executable is None and sys.executable: self.executable = os.path.normpath(sys.executable) def run(self): diff --git a/Lib/distutils/command/check.py b/Lib/distutils/command/check.py index 4ea03d3..fc7db52 100644 --- a/Lib/distutils/command/check.py +++ b/Lib/distutils/command/check.py @@ -124,7 +124,8 @@ class check(Command): def _check_rst_data(self, data): """Returns warnings when the provided data doesn't compile.""" - source_path = StringIO() + # the include and csv_table directives need this to be a path + source_path = self.distribution.script_name or 'setup.py' parser = Parser() settings = frontend.OptionParser(components=(Parser,)).get_default_values() settings.tab_width = 4 diff --git a/Lib/distutils/sysconfig.py b/Lib/distutils/sysconfig.py index de7da1d..1a4b792 100644 --- a/Lib/distutils/sysconfig.py +++ b/Lib/distutils/sysconfig.py @@ -25,7 +25,12 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) # Path to the base directory of the project. On Windows the binary may # live in project/PCBuild9. If we're dealing with an x64 Windows build, # it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) +if sys.executable: + project_base = os.path.dirname(os.path.abspath(sys.executable)) +else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + project_base = os.getcwd() if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) # PC/VS7.1 @@ -79,7 +84,12 @@ def get_python_inc(plat_specific=0, prefix=None): if os.name == "posix": if python_build: - buildir = os.path.dirname(sys.executable) + if sys.executable: + buildir = os.path.dirname(sys.executable) + else: + # sys.executable can be empty if argv[0] has been changed + # and Python is unable to retrieve the real program name + buildir = os.getcwd() if plat_specific: # python.h is located in the buildir inc_dir = buildir @@ -171,8 +181,8 @@ def customize_compiler(compiler): _osx_support.customize_compiler(_config_vars) _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ - get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + (cc, cxx, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ + get_config_vars('CC', 'CXX', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') @@ -196,7 +206,7 @@ def customize_compiler(compiler): if 'LDFLAGS' in os.environ: ldshared = ldshared + ' ' + os.environ['LDFLAGS'] if 'CFLAGS' in os.environ: - cflags = opt + ' ' + os.environ['CFLAGS'] + cflags = cflags + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] if 'CPPFLAGS' in os.environ: cpp = cpp + ' ' + os.environ['CPPFLAGS'] diff --git a/Lib/distutils/tests/includetest.rst b/Lib/distutils/tests/includetest.rst new file mode 100644 index 0000000..d7b4ae3 --- /dev/null +++ b/Lib/distutils/tests/includetest.rst @@ -0,0 +1 @@ +This should be included. diff --git a/Lib/distutils/tests/test_check.py b/Lib/distutils/tests/test_check.py index e94647f..2e2cf13 100644 --- a/Lib/distutils/tests/test_check.py +++ b/Lib/distutils/tests/test_check.py @@ -1,5 +1,6 @@ # -*- encoding: utf8 -*- """Tests for distutils.command.check.""" +import os import textwrap import unittest from test.test_support import run_unittest @@ -14,13 +15,19 @@ except ImportError: pygments = None +HERE = os.path.dirname(__file__) + + class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase): - def _run(self, metadata=None, **options): + def _run(self, metadata=None, cwd=None, **options): if metadata is None: metadata = {} + if cwd is not None: + old_dir = os.getcwd() + os.chdir(cwd) pkg_info, dist = self.create_dist(**metadata) cmd = check(dist) cmd.initialize_options() @@ -28,6 +35,8 @@ class CheckTestCase(support.LoggingSilencer, setattr(cmd, name, value) cmd.ensure_finalized() cmd.run() + if cwd is not None: + os.chdir(old_dir) return cmd def test_check_metadata(self): @@ -100,6 +109,11 @@ class CheckTestCase(support.LoggingSilencer, cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEqual(cmd._warnings, 0) + # check that includes work to test #31292 + metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst' + cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1) + self.assertEqual(cmd._warnings, 0) + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_restructuredtext_with_syntax_highlight(self): # Don't fail if there is a `code` or `code-block` directive diff --git a/Lib/distutils/tests/test_sysconfig.py b/Lib/distutils/tests/test_sysconfig.py index eb4d27c..9e2aeb8 100644 --- a/Lib/distutils/tests/test_sysconfig.py +++ b/Lib/distutils/tests/test_sysconfig.py @@ -8,8 +8,9 @@ import sys import textwrap from distutils import sysconfig +from distutils.ccompiler import get_default_compiler from distutils.tests import support -from test.test_support import TESTFN +from test.test_support import TESTFN, swap_item class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): @@ -50,6 +51,102 @@ class SysconfigTestCase(support.EnvironGuard, python_h = os.path.join(inc_dir, "Python.h") self.assertTrue(os.path.isfile(python_h), python_h) + def customize_compiler(self): + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + sysconfig_vars = { + 'AR': 'sc_ar', + 'CC': 'sc_cc', + 'CXX': 'sc_cxx', + 'ARFLAGS': '--sc-arflags', + 'CFLAGS': '--sc-cflags', + 'CCSHARED': '--sc-ccshared', + 'LDSHARED': 'sc_ldshared', + 'SO': 'sc_shutil_suffix', + } + + comp = compiler() + old_vars = dict(sysconfig._config_vars) + try: + # On macOS, disable _osx_support.customize_compiler() + sysconfig._config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + + for key, value in sysconfig_vars.items(): + sysconfig._config_vars[key] = value + sysconfig.customize_compiler(comp) + finally: + sysconfig._config_vars.clear() + sysconfig._config_vars.update(old_vars) + + return comp + + @unittest.skipUnless(get_default_compiler() == 'unix', + 'not testing if default compiler is not unix') + def test_customize_compiler(self): + # Make sure that sysconfig._config_vars is initialized + sysconfig.get_config_vars() + + os.environ['AR'] = 'env_ar' + os.environ['CC'] = 'env_cc' + os.environ['CPP'] = 'env_cpp' + os.environ['CXX'] = 'env_cxx --env-cxx-flags' + os.environ['LDSHARED'] = 'env_ldshared' + os.environ['LDFLAGS'] = '--env-ldflags' + os.environ['ARFLAGS'] = '--env-arflags' + os.environ['CFLAGS'] = '--env-cflags' + os.environ['CPPFLAGS'] = '--env-cppflags' + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'env_ar --env-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'env_cpp --env-cppflags') + self.assertEqual(comp.exes['compiler'], + 'env_cc --sc-cflags --env-cflags --env-cppflags') + self.assertEqual(comp.exes['compiler_so'], + ('env_cc --sc-cflags ' + '--env-cflags ''--env-cppflags --sc-ccshared')) + self.assertEqual(comp.exes['compiler_cxx'], + 'env_cxx --env-cxx-flags') + self.assertEqual(comp.exes['linker_exe'], + 'env_cc') + self.assertEqual(comp.exes['linker_so'], + ('env_ldshared --env-ldflags --env-cflags' + ' --env-cppflags')) + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') + + del os.environ['AR'] + del os.environ['CC'] + del os.environ['CPP'] + del os.environ['CXX'] + del os.environ['LDSHARED'] + del os.environ['LDFLAGS'] + del os.environ['ARFLAGS'] + del os.environ['CFLAGS'] + del os.environ['CPPFLAGS'] + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'sc_ar --sc-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'sc_cc -E') + self.assertEqual(comp.exes['compiler'], + 'sc_cc --sc-cflags') + self.assertEqual(comp.exes['compiler_so'], + 'sc_cc --sc-cflags --sc-ccshared') + self.assertEqual(comp.exes['compiler_cxx'], + 'sc_cxx') + self.assertEqual(comp.exes['linker_exe'], + 'sc_cc') + self.assertEqual(comp.exes['linker_so'], + 'sc_ldshared') + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') + def test_parse_makefile_base(self): self.makefile = test.test_support.TESTFN fd = open(self.makefile, 'w') diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index 690db2c..dc49d2e 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -336,7 +336,12 @@ class AddrlistClass: aslist.append('@') self.pos += 1 self.gotonext() - return EMPTYSTRING.join(aslist) + self.getdomain() + domain = self.getdomain() + if not domain: + # Invalid domain, return an empty address instead of returning a + # local part to denote failed parsing. + return EMPTYSTRING + return EMPTYSTRING.join(aslist) + domain def getdomain(self): """Get the complete domain name from an address.""" @@ -351,6 +356,10 @@ class AddrlistClass: elif self.field[self.pos] == '.': self.pos += 1 sdlist.append('.') + elif self.field[self.pos] == '@': + # bpo-34155: Don't parse domains with two `@` like + # `a@malicious.org@important.com`. + return EMPTYSTRING elif self.field[self.pos] in self.atomends: break else: diff --git a/Lib/email/feedparser.py b/Lib/email/feedparser.py index 8031ca6..298fe79 100644 --- a/Lib/email/feedparser.py +++ b/Lib/email/feedparser.py @@ -117,26 +117,6 @@ class BufferedSubFile(object): self._partial = [parts.pop()] self.pushlines(parts) - def pushlines(self, lines): - # Crack into lines, but preserve the newlines on the end of each - parts = NLCRE_crack.split(data) - # The *ahem* interesting behaviour of re.split when supplied grouping - # parentheses is that the last element of the resulting list is the - # data after the final RE. In the case of a NL/CR terminated string, - # this is the empty string. - self._partial = parts.pop() - #GAN 29Mar09 bugs 1555570, 1721862 Confusion at 8K boundary ending with \r: - # is there a \n to follow later? - if not self._partial and parts and parts[-1].endswith('\r'): - self._partial = parts.pop(-2)+parts.pop() - # parts is a list of strings, alternating between the line contents - # and the eol character(s). Gather up a list of lines after - # re-attaching the newlines. - lines = [] - for i in range(len(parts) // 2): - lines.append(parts[i*2] + parts[i*2+1]) - self.pushlines(lines) - def pushlines(self, lines): # Reverse and insert at the front of the lines. self._lines[:0] = lines[::-1] diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py index 4b4dee3..2efe44a 100644 --- a/Lib/email/test/test_email.py +++ b/Lib/email/test/test_email.py @@ -2306,6 +2306,20 @@ class TestMiscellaneous(TestEmailBase): self.assertEqual(Utils.parseaddr('<>'), ('', '')) self.assertEqual(Utils.formataddr(Utils.parseaddr('<>')), '') + def test_parseaddr_multiple_domains(self): + self.assertEqual( + Utils.parseaddr('a@b@c'), + ('', '') + ) + self.assertEqual( + Utils.parseaddr('a@b.c@c'), + ('', '') + ) + self.assertEqual( + Utils.parseaddr('a@172.17.0.1@c'), + ('', '') + ) + def test_noquote_dump(self): self.assertEqual( Utils.formataddr(('A Silly Person', 'person@dom.ain')), diff --git a/Lib/email/test/test_email_renamed.py b/Lib/email/test/test_email_renamed.py index 5a41701..206baaf 100644 --- a/Lib/email/test/test_email_renamed.py +++ b/Lib/email/test/test_email_renamed.py @@ -513,11 +513,6 @@ class TestEncoders(unittest.TestCase): msg.set_charset('us-ascii') eq(msg['content-transfer-encoding'], '7bit') - def test_default_cte(self): - eq = self.assertEqual - msg = MIMEText('hello world') - eq(msg['content-transfer-encoding'], '7bit') - def test_default_cte(self): eq = self.assertEqual # With no explicit _charset its us-ascii, and all are 7-bit diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py index 5021ebf..0955141 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -12,9 +12,9 @@ import tempfile __all__ = ["version", "bootstrap"] -_SETUPTOOLS_VERSION = "40.6.2" +_SETUPTOOLS_VERSION = "41.2.0" -_PIP_VERSION = "18.1" +_PIP_VERSION = "19.2.3" _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION), diff --git a/Lib/ensurepip/_bundled/pip-18.1-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-18.1-py2.py3-none-any.whl deleted file mode 100644 index c3c146f..0000000 Binary files a/Lib/ensurepip/_bundled/pip-18.1-py2.py3-none-any.whl and /dev/null differ diff --git a/Lib/ensurepip/_bundled/pip-19.2.3-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/pip-19.2.3-py2.py3-none-any.whl new file mode 100644 index 0000000..8118df8 Binary files /dev/null and b/Lib/ensurepip/_bundled/pip-19.2.3-py2.py3-none-any.whl differ diff --git a/Lib/ensurepip/_bundled/setuptools-40.6.2-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/setuptools-40.6.2-py2.py3-none-any.whl deleted file mode 100644 index 4c8a619..0000000 Binary files a/Lib/ensurepip/_bundled/setuptools-40.6.2-py2.py3-none-any.whl and /dev/null differ diff --git a/Lib/ensurepip/_bundled/setuptools-41.2.0-py2.py3-none-any.whl b/Lib/ensurepip/_bundled/setuptools-41.2.0-py2.py3-none-any.whl new file mode 100644 index 0000000..82df6f6 Binary files /dev/null and b/Lib/ensurepip/_bundled/setuptools-41.2.0-py2.py3-none-any.whl differ diff --git a/Lib/httplib.py b/Lib/httplib.py index 60a8fb4..79532b9 100644 --- a/Lib/httplib.py +++ b/Lib/httplib.py @@ -247,6 +247,16 @@ _MAXHEADERS = 100 _is_legal_header_name = re.compile(r'\A[^:\s][^:\r\n]*\Z').match _is_illegal_header_value = re.compile(r'\n(?![ \t])|\r(?![ \t\n])').search +# These characters are not allowed within HTTP URL paths. +# See https://tools.ietf.org/html/rfc3986#section-3.3 and the +# https://tools.ietf.org/html/rfc3986#appendix-A pchar definition. +# Prevents CVE-2019-9740. Includes control characters such as \r\n. +# Restrict non-ASCII characters above \x7f (0x80-0xff). +_contains_disallowed_url_pchar_re = re.compile('[\x00-\x20\x7f-\xff]') +# Arguably only these _should_ allowed: +# _is_allowed_url_pchars_re = re.compile(r"^[/!$&'()*+,;=:@%a-zA-Z0-9._~-]+$") +# We are more lenient for assumed real world compatibility purposes. + # We always set the Content-Length header for these methods because some # servers will otherwise respond with a 411 _METHODS_EXPECTING_BODY = {'PATCH', 'POST', 'PUT'} @@ -923,13 +933,15 @@ class HTTPConnection: else: raise CannotSendRequest() - # Save the method we use, we need it later in the response phase + # Save the method for use later in the response phase self._method = method - if not url: - url = '/' - hdr = '%s %s %s' % (method, url, self._http_vsn_str) - self._output(hdr) + url = url or '/' + self._validate_path(url) + + request = '%s %s %s' % (method, url, self._http_vsn_str) + + self._output(self._encode_request(request)) if self._http_vsn == 11: # Issue some standard headers for better HTTP/1.1 compliance @@ -1002,6 +1014,21 @@ class HTTPConnection: # For HTTP/1.0, the server will assume "not chunked" pass + def _encode_request(self, request): + # On Python 2, request is already encoded (default) + return request + + def _validate_path(self, url): + """Validate a url for putrequest.""" + # Prevent CVE-2019-9740. + match = _contains_disallowed_url_pchar_re.search(url) + if match: + msg = ( + "URL can't contain control characters. {url!r} " + "(found at least {matched!r})" + ).format(matched=match.group(), url=url) + raise InvalidURL(msg) + def putheader(self, header, *values): """Send a request header line to the server. diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py index 2aba46e..872bece 100644 --- a/Lib/idlelib/IOBinding.py +++ b/Lib/idlelib/IOBinding.py @@ -383,6 +383,8 @@ class IOBinding: try: with open(filename, "wb") as f: f.write(chars) + f.flush() + os.fsync(f.fileno()) return True except IOError as msg: tkMessageBox.showerror("I/O Error", str(msg), diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt index 7eb30a1..d4560b8 100644 --- a/Lib/idlelib/NEWS.txt +++ b/Lib/idlelib/NEWS.txt @@ -1,8 +1,16 @@ Since 2.7.13, only severe bugs are fixed on the 2.7 branch. +What's New in IDLE 2.7.17? +========================== +*Release date: 2019-07-??* + +bpo-36807: When saving a file, call file.flush() and os.fsync() +so bits are flushed to e.g. a USB drive. + + What's New in IDLE 2.7.16? ========================== -*Release date: 2019-01-01?* +*Release date: 2019-03-02* bpo-31500: Default fonts now are scaled on HiDPI displays. diff --git a/Lib/idlelib/SearchDialogBase.py b/Lib/idlelib/SearchDialogBase.py index 651e7f4..9eb8b22 100644 --- a/Lib/idlelib/SearchDialogBase.py +++ b/Lib/idlelib/SearchDialogBase.py @@ -52,6 +52,7 @@ class SearchDialogBase: else: self.top.deiconify() self.top.tkraise() + self.top.transient(text.winfo_toplevel()) if searchphrase: self.ent.delete(0,"end") self.ent.insert("end",searchphrase) @@ -64,6 +65,7 @@ class SearchDialogBase: "Put dialog away for later use." if self.top: self.top.grab_release() + self.top.transient('') self.top.withdraw() def create_widgets(self): diff --git a/Lib/idlelib/idle_test/test_searchdialogbase.py b/Lib/idlelib/idle_test/test_searchdialogbase.py index 32abfe6..59b9bbf 100644 --- a/Lib/idlelib/idle_test/test_searchdialogbase.py +++ b/Lib/idlelib/idle_test/test_searchdialogbase.py @@ -6,7 +6,7 @@ testing skipping of suite when self.needwrapbutton is false. ''' import unittest from test.test_support import requires -from Tkinter import Tk, Toplevel, Frame ## BooleanVar, StringVar +from Tkinter import Text, Tk, Toplevel, Frame ## BooleanVar, StringVar from idlelib import SearchEngine as se from idlelib import SearchDialogBase as sdb from idlelib.idle_test.mock_idle import Func @@ -45,16 +45,17 @@ class SearchDialogBaseTest(unittest.TestCase): # open calls create_widgets, which needs default_command self.dialog.default_command = None - # Since text parameter of .open is not used in base class, - # pass dummy 'text' instead of tk.Text(). - self.dialog.open('text') + toplevel = Toplevel(self.root) + text = Text(toplevel) + self.dialog.open(text) self.assertEqual(self.dialog.top.state(), 'normal') self.dialog.close() self.assertEqual(self.dialog.top.state(), 'withdrawn') - self.dialog.open('text', searchphrase="hello") + self.dialog.open(text, searchphrase="hello") self.assertEqual(self.dialog.ent.get(), 'hello') - self.dialog.close() + toplevel.update_idletasks() + toplevel.destroy() def test_create_widgets(self): self.dialog.create_entries = Func() diff --git a/Lib/idlelib/keybindingDialog.py b/Lib/idlelib/keybindingDialog.py index 755f1af..9713c79 100644 --- a/Lib/idlelib/keybindingDialog.py +++ b/Lib/idlelib/keybindingDialog.py @@ -182,7 +182,7 @@ class GetKeysDialog(Toplevel): def LoadFinalKeyList(self): #these tuples are also available for use in validity checks - self.functionKeys=('F1','F2','F2','F4','F5','F6','F7','F8','F9', + self.functionKeys=('F1','F2','F3','F4','F5','F6','F7','F8','F9', 'F10','F11','F12') self.alphanumKeys=tuple(string.ascii_lowercase+string.digits) self.punctuationKeys=tuple('~!@#%^&*()_-+={}[]|;:,.<>/?') diff --git a/Lib/lib-tk/test/test_ttk/test_widgets.py b/Lib/lib-tk/test/test_ttk/test_widgets.py index 3d5683c..47cd7af 100644 --- a/Lib/lib-tk/test/test_ttk/test_widgets.py +++ b/Lib/lib-tk/test/test_ttk/test_widgets.py @@ -331,7 +331,12 @@ class EntryTest(AbstractWidgetTest, unittest.TestCase): self.entry.wait_visibility() self.entry.update_idletasks() - self.assertEqual(self.entry.identify(5, 5), "textarea") + # bpo-27313: macOS Cocoa widget differs from X, allow either + if sys.platform == 'darwin': + self.assertIn(self.entry.identify(5, 5), + ("textarea", "Combobox.button") ) + else: + self.assertEqual(self.entry.identify(5, 5), "textarea") self.assertEqual(self.entry.identify(-1, -1), "") self.assertRaises(tkinter.TclError, self.entry.identify, None, 5) @@ -1675,9 +1680,5 @@ tests_gui = ( SizegripTest, TreeviewTest, WidgetTest, ) -tests_gui = ( - TreeviewTest, - ) - if __name__ == "__main__": run_unittest(*tests_gui) diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py index 98386c5..8a40deb 100644 --- a/Lib/lib2to3/refactor.py +++ b/Lib/lib2to3/refactor.py @@ -15,6 +15,7 @@ __author__ = "Guido van Rossum " # Python imports import os +import pkgutil import sys import logging import operator @@ -33,13 +34,12 @@ from . import btm_matcher as bm def get_all_fix_names(fixer_pkg, remove_prefix=True): """Return a sorted list of all available fix names in the given package.""" pkg = __import__(fixer_pkg, [], [], ["*"]) - fixer_dir = os.path.dirname(pkg.__file__) fix_names = [] - for name in sorted(os.listdir(fixer_dir)): - if name.startswith("fix_") and name.endswith(".py"): + for finder, name, ispkg in pkgutil.iter_modules(pkg.__path__): + if name.startswith("fix_"): if remove_prefix: name = name[4:] - fix_names.append(name[:-3]) + fix_names.append(name) return fix_names diff --git a/Lib/msilib/__init__.py b/Lib/msilib/__init__.py index 0352b60..9520dfc 100644 --- a/Lib/msilib/__init__.py +++ b/Lib/msilib/__init__.py @@ -276,7 +276,7 @@ class Directory: if Win64: flags |= 256 if keyfile: - keyid = self.cab.gen_id(self.absolute, keyfile) + keyid = self.cab.gen_id(keyfile) self.keyfiles[keyfile] = keyid else: keyid = None diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py index b9e9cd7..b8e0f64 100644 --- a/Lib/sqlite3/test/factory.py +++ b/Lib/sqlite3/test/factory.py @@ -159,19 +159,24 @@ class RowFactoryTests(unittest.TestCase): row_1 = self.con.execute("select 1 as a, 2 as b").fetchone() row_2 = self.con.execute("select 1 as a, 2 as b").fetchone() row_3 = self.con.execute("select 1 as a, 3 as b").fetchone() + row_4 = self.con.execute("select 1 as b, 2 as a").fetchone() + row_5 = self.con.execute("select 2 as b, 1 as a").fetchone() - self.assertEqual(row_1, row_1) - self.assertEqual(row_1, row_2) - self.assertTrue(row_2 != row_3) + self.assertTrue(row_1 == row_1) + self.assertTrue(row_1 == row_2) + self.assertFalse(row_1 == row_3) + self.assertFalse(row_1 == row_4) + self.assertFalse(row_1 == row_5) + self.assertFalse(row_1 == object()) self.assertFalse(row_1 != row_1) self.assertFalse(row_1 != row_2) - self.assertFalse(row_2 == row_3) + self.assertTrue(row_1 != row_3) + self.assertTrue(row_1 != row_4) + self.assertTrue(row_1 != row_5) + self.assertTrue(row_1 != object()) - self.assertEqual(row_1, row_2) self.assertEqual(hash(row_1), hash(row_2)) - self.assertNotEqual(row_1, row_3) - self.assertNotEqual(hash(row_1), hash(row_3)) def CheckSqliteRowAsSequence(self): """ Checks if the row object can act like a sequence """ diff --git a/Lib/test/bisect_cmd.py b/Lib/test/bisect_cmd.py index 1bf32ef..5028ed2 100755 --- a/Lib/test/bisect_cmd.py +++ b/Lib/test/bisect_cmd.py @@ -4,17 +4,17 @@ Command line tool to bisect failing CPython tests. Find the test_os test method which alters the environment: - ./python -m test.bisect --fail-env-changed test_os + ./python -m test.bisect_cmd --fail-env-changed test_os Find a reference leak in "test_os", write the list of failing tests into the "bisect" file: - ./python -m test.bisect -o bisect -R 3:3 test_os + ./python -m test.bisect_cmd -o bisect -R 3:3 test_os Load an existing list of tests from a file using -i option: ./python -m test --list-cases -m FileTests test_os > tests - ./python -m test.bisect -i tests test_os + ./python -m test.bisect_cmd -i tests test_os """ from __future__ import print_function diff --git a/Lib/test/capath/efa5f9c3.0 b/Lib/test/capath/efa5f9c3.0 new file mode 100644 index 0000000..2b17607 --- /dev/null +++ b/Lib/test/capath/efa5f9c3.0 @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9zCCA9+gAwIBAgIUH98b4Fw/DyugC9cV7VK7ZODzHsIwDQYJKoZIhvcNAQEL +BQAwgYoxCzAJBgNVBAYTAlhZMRcwFQYDVQQIDA5DYXN0bGUgQW50aHJheDEYMBYG +A1UEBwwPQXJndW1lbnQgQ2xpbmljMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0aG9udGVzdC5uZXQw +HhcNMTkwNTA4MDEwMjQzWhcNMjcwNzI0MDEwMjQzWjCBijELMAkGA1UEBhMCWFkx +FzAVBgNVBAgMDkNhc3RsZSBBbnRocmF4MRgwFgYDVQQHDA9Bcmd1bWVudCBDbGlu +aWMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQD +DBpzZWxmLXNpZ25lZC5weXRob250ZXN0Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMKdJlyCThkahwoBb7pl5q64Pe9Fn5jrIvzsveHTc97TpjV2 +RLfICnXKrltPk/ohkVl6K5SUZQZwMVzFubkyxE0nZPHYHlpiKWQxbsYVkYv01rix +IFdLvaxxbGYke2jwQao31s4o61AdlsfK1SdpHQUynBBMssqI3SB4XPmcA7e+wEEx +jxjVish4ixA1vuIZOx8yibu+CFCf/geEjoBMF3QPdzULzlrCSw8k/45iZCSoNbvK +DoL4TVV07PHOxpheDh8ZQmepGvU6pVqhb9m4lgmV0OGWHgozd5Ur9CbTVDmxIEz3 +TSoRtNJK7qtyZdGNqwjksQxgZTjM/d/Lm/BJG99AiOmYOjsl9gbQMZgvQmMAtUsI +aMJnQuZ6R+KEpW/TR5qSKLWZSG45z/op+tzI2m+cE6HwTRVAWbcuJxcAA55MZjqU +OOOu3BBYMjS5nf2sQ9uoXsVBFH7i0mQqoW1SLzr9opI8KsWwFxQmO2vBxWYaN+lH +OmwBZBwyODIsmI1YGXmTp09NxRYz3Qe5GCgFzYowpMrcxUC24iduIdMwwhRM7rKg +7GtIWMSrFfuI1XCLRmSlhDbhNN6fVg2f8Bo9PdH9ihiIyxSrc+FOUasUYCCJvlSZ +8hFUlLvcmrZlWuazohm0lsXuMK1JflmQr/DA/uXxP9xzFfRy+RU3jDyxJbRHAgMB +AAGjUzBRMB0GA1UdDgQWBBSQJyxiPMRK01i+0BsV9zUwDiBaHzAfBgNVHSMEGDAW +gBSQJyxiPMRK01i+0BsV9zUwDiBaHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCR+7a7N/m+WLkxPPIA/CB4MOr2Uf8ixTv435Nyv6rXOun0+lTP +ExSZ0uYQ+L0WylItI3cQHULldDueD+s8TGzxf5woaLKf6tqyr0NYhKs+UeNEzDnN +9PHQIhX0SZw3XyXGUgPNBfRCg2ZDdtMMdOU4XlQN/IN/9hbYTrueyY7eXq9hmtI9 +1srftAMqr9SR1JP7aHI6DVgrEsZVMTDnfT8WmLSGLlY1HmGfdEn1Ip5sbo9uSkiH +AEPgPfjYIvR5LqTOMn4KsrlZyBbFIDh9Sl99M1kZzgH6zUGVLCDg1y6Cms69fx/e +W1HoIeVkY4b4TY7Bk7JsqyNhIuqu7ARaxkdaZWhYaA2YyknwANdFfNpfH+elCLIk +BUt5S3f4i7DaUePTvKukCZiCq4Oyln7RcOn5If73wCeLB/ZM9Ei1HforyLWP1CN8 +XLfpHaoeoPSWIveI0XHUl65LsPN2UbMbul/F23hwl+h8+BLmyAS680Yhn4zEN6Ku +B7Po90HoFa1Du3bmx4jsN73UkT/dwMTi6K072FbipnC1904oGlWmLwvAHvrtxxmL +Pl3pvEaZIu8wa/PNF6Y7J7VIewikIJq6Ta6FrWeFfzMWOj2qA1ZZi6fUaDSNYvuV +J5quYKCc/O+I/yDDf8wyBbZ/gvUXzUHTMYGG+bFrn1p7XDbYYeEJ6R/xEg== +-----END CERTIFICATE----- diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index ae1c07b..12de99b 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -6,6 +6,7 @@ import errno import re import sys import traceback +import warnings def normalize_text(text): @@ -142,7 +143,11 @@ def collect_platform(info_add): info_add('platform.python_implementation', platform.python_implementation()) info_add('platform.platform', - platform.platform(aliased=True, terse=True)) + platform.platform(aliased=True)) + + libc_ver = ('%s %s' % platform.libc_ver()).strip() + if libc_ver: + info_add('platform.libc_ver', libc_ver) def collect_locale(info_add): @@ -376,9 +381,17 @@ def collect_time(info_add): copy_attributes(info_add, time, 'time.%s', attributes) if hasattr(time, 'get_clock_info'): - for clock in ('time', 'perf_counter'): - tinfo = time.get_clock_info(clock) - info_add('time.get_clock_info(%s)' % clock, tinfo) + for clock in ('clock', 'monotonic', 'perf_counter', + 'process_time', 'thread_time', 'time'): + try: + # prevent DeprecatingWarning on get_clock_info('clock') + with warnings.catch_warnings(record=True): + clock_info = time.get_clock_info(clock) + except ValueError: + # missing clock like time.thread_time() + pass + else: + info_add('time.get_clock_info(%s)' % clock, clock_info) def collect_datetime(info_add): @@ -407,7 +420,10 @@ def collect_sysconfig(info_add): 'OPT', 'PY_CFLAGS', 'PY_CFLAGS_NODIST', + 'PY_CORE_LDFLAGS', 'PY_LDFLAGS', + 'PY_LDFLAGS_NODIST', + 'PY_STDMODULE_CFLAGS', 'Py_DEBUG', 'Py_ENABLE_SHARED', 'SHELL', @@ -423,10 +439,15 @@ def collect_sysconfig(info_add): def collect_ssl(info_add): + import os try: import ssl except ImportError: return + try: + import _ssl + except ImportError: + _ssl = None def format_attr(attr, value): if attr.startswith('OP_'): @@ -443,6 +464,61 @@ def collect_ssl(info_add): ) copy_attributes(info_add, ssl, 'ssl.%s', attributes, formatter=format_attr) + options_names = [] + protocol_names = {} + verify_modes = {} + for name in dir(ssl): + if name.startswith('OP_'): + options_names.append((name, getattr(ssl, name))) + elif name.startswith('PROTOCOL_'): + protocol_names[getattr(ssl, name)] = name + elif name.startswith('CERT_'): + verify_modes[getattr(ssl, name)] = name + options_names.sort(key=lambda item: item[1], reverse=True) + + def formatter(attr_name, value): + if attr_name == 'options': + options_text = [] + for opt_name, opt_value in options_names: + if value & opt_value: + options_text.append(opt_name) + value &= ~opt_value + if value: + options_text.append(str(value)) + return '|' .join(options_text) + elif attr_name == 'verify_mode': + return verify_modes.get(value, value) + elif attr_name == 'protocol': + return protocol_names.get(value, value) + else: + return value + + for name, ctx in ( + ('SSLContext(PROTOCOL_TLS)', ssl.SSLContext(ssl.PROTOCOL_TLS)), + ('default_https_context', ssl._create_default_https_context()), + ('stdlib_context', ssl._create_stdlib_context()), + ): + attributes = ( + 'minimum_version', + 'maximum_version', + 'protocol', + 'options', + 'verify_mode', + ) + copy_attributes(info_add, ctx, 'ssl.%s.%%s' % name, attributes, formatter=formatter) + + env_names = ["OPENSSL_CONF", "SSLKEYLOGFILE"] + if _ssl is not None and hasattr(_ssl, 'get_default_verify_paths'): + parts = _ssl.get_default_verify_paths() + env_names.extend((parts[0], parts[2])) + + for name in env_names: + try: + value = os.environ[name] + except KeyError: + continue + info_add('ssl.environ[%s]' % name, value) + def collect_socket(info_add): import socket @@ -513,6 +589,8 @@ def collect_resource(info_add): value = resource.getrlimit(key) info_add('resource.%s' % name, value) + call_func(info_add, 'resource.pagesize', resource, 'getpagesize') + def collect_test_socket(info_add): try: @@ -553,10 +631,17 @@ def collect_cc(info_add): except ImportError: args = CC.split() args.append('--version') - proc = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) + try: + proc = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + except OSError: + # Cannot run the compiler, for example when Python has been + # cross-compiled and installed on the target platform where the + # compiler is missing. + return + stdout = proc.communicate()[0] if proc.returncode: # CC --version failed: ignore error @@ -567,6 +652,35 @@ def collect_cc(info_add): info_add('CC.version', text) +def collect_gdbm(info_add): + try: + from _gdbm import _GDBM_VERSION + except ImportError: + return + + info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION))) + + +def collect_get_config(info_add): + # Dump global configuration variables, _PyCoreConfig + # and _PyMainInterpreterConfig + try: + from _testinternalcapi import get_configs + except ImportError: + return + + all_configs = get_configs() + for config_type in sorted(all_configs): + config = all_configs[config_type] + for key in sorted(config): + info_add('%s[%s]' % (config_type, key), repr(config[key])) + + +def collect_subprocess(info_add): + import subprocess + copy_attributes(info_add, subprocess, 'subprocess.%s', ('_USE_POSIX_SPAWN',)) + + def collect_info(info): error = False info_add = info.add @@ -594,6 +708,9 @@ def collect_info(info): collect_testcapi, collect_resource, collect_cc, + collect_gdbm, + collect_get_config, + collect_subprocess, # Collecting from tests should be last as they have side effects. collect_test_socket, diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 70c5122..5b0b3e4 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -375,7 +375,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, 'runleaks', 'huntrleaks=', 'memlimit=', 'randseed=', 'multiprocess=', 'slaveargs=', 'forever', 'header', 'pgo', 'failfast', 'match=', 'testdir=', 'list-tests', 'list-cases', - 'coverage', 'matchfile=', 'fail-env-changed']) + 'coverage', 'matchfile=', 'fail-env-changed', 'cleanup']) except getopt.error, msg: usage(2, msg) @@ -388,6 +388,7 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, list_tests = False list_cases_opt = False fail_env_changed = False + cleanup_tests = False for o, a in opts: if o in ('-h', '--help'): usage(0) @@ -490,6 +491,8 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, list_cases_opt = True elif o == '--fail-env-changed': fail_env_changed = True + elif o == '--cleanup': + cleanup_tests = True else: print >>sys.stderr, ("No handler for option {}. Please " "report this as a bug at http://bugs.python.org.").format(o) @@ -527,10 +530,24 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, print >>sys.stderr, msg sys.exit(2) + if cleanup_tests: + import glob + + os.chdir(support.SAVEDCWD) + path = os.path.join(TEMPDIR, 'test_python_*') + print("Cleanup %s directory" % TEMPDIR) + for name in glob.glob(path): + if os.path.isdir(name): + print("Remove directory: %s" % name) + support.rmtree(name) + else: + print("Remove file: %s" % name) + support.unlink(name) + sys.exit(0) + + if slaveargs is not None: args, kwargs = json.loads(slaveargs) - if kwargs['huntrleaks']: - warm_caches() if testdir: kwargs['testdir'] = testdir try: @@ -541,9 +558,6 @@ def main(tests=None, testdir=None, verbose=0, quiet=False, print json.dumps(result) sys.exit(0) - if huntrleaks: - warm_caches() - good = [] bad = [] skipped = [] @@ -1332,7 +1346,7 @@ def runtest_inner(test, verbose, quiet, huntrleaks=False, pgo=False, testdir=Non indirect_test = getattr(the_module, "test_main", None) if huntrleaks: refleak = dash_R(the_module, test, indirect_test, - huntrleaks) + huntrleaks, quiet) else: if indirect_test is not None: indirect_test() @@ -1425,7 +1439,7 @@ def cleanup_test_droppings(testname, verbose): print >> sys.stderr, ("%r left behind %s %r and it couldn't be " "removed: %s" % (testname, kind, name, msg)) -def dash_R(the_module, test, indirect_test, huntrleaks): +def dash_R(the_module, test, indirect_test, huntrleaks, quiet): """Run a test multiple times, looking for reference leaks. Returns: @@ -1438,6 +1452,10 @@ def dash_R(the_module, test, indirect_test, huntrleaks): raise Exception("Tracking reference leaks requires a debug build " "of Python") + # Avoid false positives due to various caches + # filling slowly with random data: + warm_caches() + # Save current values for dash_R_cleanup() to restore. fs = warnings.filters[:] ps = copy_reg.dispatch_table.copy() @@ -1457,6 +1475,14 @@ def dash_R(the_module, test, indirect_test, huntrleaks): for obj in abc.__subclasses__() + [abc]: abcs[obj] = obj._abc_registry.copy() + # bpo-31217: Integer pool to get a single integer object for the same + # value. The pool is used to prevent false alarm when checking for memory + # block leaks. Fill the pool with values in -1000..1000 which are the most + # common (reference, memory block, file descriptor) differences. + int_pool = {value: value for value in range(-1000, 1000)} + def get_pooled_int(value): + return int_pool.setdefault(value, value) + if indirect_test: def run_the_test(): indirect_test() @@ -1467,27 +1493,39 @@ def dash_R(the_module, test, indirect_test, huntrleaks): deltas = [] nwarmup, ntracked, fname = huntrleaks fname = os.path.join(support.SAVEDCWD, fname) + + # Pre-allocate to ensure that the loop doesn't allocate anything new repcount = nwarmup + ntracked - rc_deltas = [0] * ntracked - fd_deltas = [0] * ntracked + rc_deltas = [0] * repcount + fd_deltas = [0] * repcount + rep_range = list(range(repcount)) + + if not quiet: + print >> sys.stderr, "beginning", repcount, "repetitions" + print >> sys.stderr, ("1234567890"*(repcount//10 + 1))[:repcount] - print >> sys.stderr, "beginning", repcount, "repetitions" - print >> sys.stderr, ("1234567890"*(repcount//10 + 1))[:repcount] dash_R_cleanup(fs, ps, pic, zdc, abcs) + # initialize variables to make pyflakes quiet rc_before = fd_before = 0 - for i in range(repcount): + + for i in rep_range: run_the_test() - sys.stderr.write('.') + + if not quiet: + sys.stderr.write('.') + dash_R_cleanup(fs, ps, pic, zdc, abcs) + rc_after = sys.gettotalrefcount() fd_after = support.fd_count() - if i >= nwarmup: - rc_deltas[i - nwarmup] = rc_after - rc_before - fd_deltas[i - nwarmup] = fd_after - fd_before + rc_deltas[i] = get_pooled_int(rc_after - rc_before) + fd_deltas[i] = get_pooled_int(fd_after - fd_before) rc_before = rc_after fd_before = fd_after - print >> sys.stderr + + if not quiet: + print >> sys.stderr # These checkers return False on success, True on failure def check_rc_deltas(deltas): @@ -1513,6 +1551,7 @@ def dash_R(the_module, test, indirect_test, huntrleaks): (rc_deltas, 'references', check_rc_deltas), (fd_deltas, 'file descriptors', check_fd_deltas) ]: + deltas = deltas[nwarmup:] if checker(deltas): msg = '%s leaked %s %s, sum=%s' % (test, deltas, item_name, sum(deltas)) print >> sys.stderr, msg @@ -1647,7 +1686,7 @@ def clear_caches(): ctypes._reset_cache() # Collect cyclic trash. - gc.collect() + support.gc_collect() def warm_caches(): """Create explicitly internal singletons which are created on demand diff --git a/Lib/test/selfsigned_pythontestdotnet.pem b/Lib/test/selfsigned_pythontestdotnet.pem index b6d259b..2b17607 100644 --- a/Lib/test/selfsigned_pythontestdotnet.pem +++ b/Lib/test/selfsigned_pythontestdotnet.pem @@ -1,16 +1,34 @@ -----BEGIN CERTIFICATE----- -MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV -BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv -bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG -A1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMwIQYDVQQKDBpQeXRo -b24gU29mdHdhcmUgRm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0 -aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ -Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm -Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv -EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl -bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN -AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h -TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 -C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= +MIIF9zCCA9+gAwIBAgIUH98b4Fw/DyugC9cV7VK7ZODzHsIwDQYJKoZIhvcNAQEL +BQAwgYoxCzAJBgNVBAYTAlhZMRcwFQYDVQQIDA5DYXN0bGUgQW50aHJheDEYMBYG +A1UEBwwPQXJndW1lbnQgQ2xpbmljMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0aG9udGVzdC5uZXQw +HhcNMTkwNTA4MDEwMjQzWhcNMjcwNzI0MDEwMjQzWjCBijELMAkGA1UEBhMCWFkx +FzAVBgNVBAgMDkNhc3RsZSBBbnRocmF4MRgwFgYDVQQHDA9Bcmd1bWVudCBDbGlu +aWMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQD +DBpzZWxmLXNpZ25lZC5weXRob250ZXN0Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMKdJlyCThkahwoBb7pl5q64Pe9Fn5jrIvzsveHTc97TpjV2 +RLfICnXKrltPk/ohkVl6K5SUZQZwMVzFubkyxE0nZPHYHlpiKWQxbsYVkYv01rix +IFdLvaxxbGYke2jwQao31s4o61AdlsfK1SdpHQUynBBMssqI3SB4XPmcA7e+wEEx +jxjVish4ixA1vuIZOx8yibu+CFCf/geEjoBMF3QPdzULzlrCSw8k/45iZCSoNbvK +DoL4TVV07PHOxpheDh8ZQmepGvU6pVqhb9m4lgmV0OGWHgozd5Ur9CbTVDmxIEz3 +TSoRtNJK7qtyZdGNqwjksQxgZTjM/d/Lm/BJG99AiOmYOjsl9gbQMZgvQmMAtUsI +aMJnQuZ6R+KEpW/TR5qSKLWZSG45z/op+tzI2m+cE6HwTRVAWbcuJxcAA55MZjqU +OOOu3BBYMjS5nf2sQ9uoXsVBFH7i0mQqoW1SLzr9opI8KsWwFxQmO2vBxWYaN+lH +OmwBZBwyODIsmI1YGXmTp09NxRYz3Qe5GCgFzYowpMrcxUC24iduIdMwwhRM7rKg +7GtIWMSrFfuI1XCLRmSlhDbhNN6fVg2f8Bo9PdH9ihiIyxSrc+FOUasUYCCJvlSZ +8hFUlLvcmrZlWuazohm0lsXuMK1JflmQr/DA/uXxP9xzFfRy+RU3jDyxJbRHAgMB +AAGjUzBRMB0GA1UdDgQWBBSQJyxiPMRK01i+0BsV9zUwDiBaHzAfBgNVHSMEGDAW +gBSQJyxiPMRK01i+0BsV9zUwDiBaHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCR+7a7N/m+WLkxPPIA/CB4MOr2Uf8ixTv435Nyv6rXOun0+lTP +ExSZ0uYQ+L0WylItI3cQHULldDueD+s8TGzxf5woaLKf6tqyr0NYhKs+UeNEzDnN +9PHQIhX0SZw3XyXGUgPNBfRCg2ZDdtMMdOU4XlQN/IN/9hbYTrueyY7eXq9hmtI9 +1srftAMqr9SR1JP7aHI6DVgrEsZVMTDnfT8WmLSGLlY1HmGfdEn1Ip5sbo9uSkiH +AEPgPfjYIvR5LqTOMn4KsrlZyBbFIDh9Sl99M1kZzgH6zUGVLCDg1y6Cms69fx/e +W1HoIeVkY4b4TY7Bk7JsqyNhIuqu7ARaxkdaZWhYaA2YyknwANdFfNpfH+elCLIk +BUt5S3f4i7DaUePTvKukCZiCq4Oyln7RcOn5If73wCeLB/ZM9Ei1HforyLWP1CN8 +XLfpHaoeoPSWIveI0XHUl65LsPN2UbMbul/F23hwl+h8+BLmyAS680Yhn4zEN6Ku +B7Po90HoFa1Du3bmx4jsN73UkT/dwMTi6K072FbipnC1904oGlWmLwvAHvrtxxmL +Pl3pvEaZIu8wa/PNF6Y7J7VIewikIJq6Ta6FrWeFfzMWOj2qA1ZZi6fUaDSNYvuV +J5quYKCc/O+I/yDDf8wyBbZ/gvUXzUHTMYGG+bFrn1p7XDbYYeEJ6R/xEg== -----END CERTIFICATE----- diff --git a/Lib/test/ssl_servers.py b/Lib/test/ssl_servers.py index a312e28..a502305 100644 --- a/Lib/test/ssl_servers.py +++ b/Lib/test/ssl_servers.py @@ -42,6 +42,11 @@ class HTTPSServer(_HTTPServer): raise return sslconn, addr + def handle_error(self, request, client_address): + "Suppose noisy error output by default." + if support.verbose: + _HTTPServer.handle_error(self, request, client_address) + class RootedHTTPRequestHandler(SimpleHTTPRequestHandler): # need to override translate_path to get a known root, # instead of using os.curdir, since the test could be diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 9effddd..ccc11c1 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -720,6 +720,10 @@ else: # module name. TESTFN = "{}_{}_tmp".format(TESTFN, os.getpid()) +# Define the URL of a dedicated HTTP server for the network tests. +# The URL must use clear-text HTTP: no redirection to encrypted HTTPS. +TEST_HTTP_URL = "http://www.pythontest.net" + # Save the initial cwd SAVEDCWD = os.getcwd() diff --git a/Lib/test/test_cookielib.py b/Lib/test/test_cookielib.py index f2dd972..a93bbfb 100644 --- a/Lib/test/test_cookielib.py +++ b/Lib/test/test_cookielib.py @@ -368,6 +368,7 @@ class CookieTests(TestCase): ("http://foo.bar.com/", ".foo.bar.com", True), ("http://foo.bar.com/", "foo.bar.com", True), ("http://foo.bar.com/", ".bar.com", True), + ("http://foo.bar.com/", "bar.com", True), ("http://foo.bar.com/", "com", True), ("http://foo.com/", "rhubarb.foo.com", False), ("http://foo.com/", ".foo.com", True), @@ -378,6 +379,8 @@ class CookieTests(TestCase): ("http://foo/", "foo", True), ("http://foo/", "foo.local", True), ("http://foo/", ".local", True), + ("http://barfoo.com", ".foo.com", False), + ("http://barfoo.com", "foo.com", False), ]: request = urllib2.Request(url) r = pol.domain_return_ok(domain, request) @@ -646,6 +649,35 @@ class CookieTests(TestCase): req = Request("http://www.example.com") self.assertEqual(request_path(req), "/") + def test_path_prefix_match(self): + from cookielib import CookieJar, DefaultCookiePolicy + from urllib2 import Request + + pol = DefaultCookiePolicy() + strict_ns_path_pol = DefaultCookiePolicy(strict_ns_set_path=True) + + c = CookieJar(pol) + base_url = "http://bar.com" + interact_netscape(c, base_url, 'spam=eggs; Path=/foo') + cookie = c._cookies['bar.com']['/foo']['spam'] + + for path, ok in [('/foo', True), + ('/foo/', True), + ('/foo/bar', True), + ('/', False), + ('/foobad/foo', False)]: + url = '{0}{1}'.format(base_url, path) + req = Request(url) + h = interact_netscape(c, url) + if ok: + self.assertIn('spam=eggs', h, + "cookie not set for {0}".format(path)) + self.assertTrue(strict_ns_path_pol.set_ok_path(cookie, req)) + else: + self.assertNotIn('spam=eggs', h, + "cookie set for {0}".format(path)) + self.assertFalse(strict_ns_path_pol.set_ok_path(cookie, req)) + def test_request_port(self): from urllib2 import Request from cookielib import request_port, DEFAULT_HTTP_PORT @@ -938,6 +970,33 @@ class CookieTests(TestCase): c.add_cookie_header(req) self.assertFalse(req.has_header("Cookie")) + c.clear() + + pol.set_blocked_domains([]) + req = Request("http://acme.com/") + res = FakeResponse(headers, "http://acme.com/") + cookies = c.make_cookies(res, req) + c.extract_cookies(res, req) + self.assertEqual(len(c), 1) + + req = Request("http://acme.com/") + c.add_cookie_header(req) + self.assertTrue(req.has_header("Cookie")) + + req = Request("http://badacme.com/") + c.add_cookie_header(req) + self.assertFalse(pol.return_ok(cookies[0], req)) + self.assertFalse(req.has_header("Cookie")) + + p = pol.set_blocked_domains(["acme.com"]) + req = Request("http://acme.com/") + c.add_cookie_header(req) + self.assertFalse(req.has_header("Cookie")) + + req = Request("http://badacme.com/") + c.add_cookie_header(req) + self.assertFalse(req.has_header("Cookie")) + def test_secure(self): from cookielib import CookieJar, DefaultCookiePolicy diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index bbc52aa..dc75a21 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -403,6 +403,14 @@ class OperatorsTest(unittest.TestCase): a.setstate(100) self.assertEqual(a.getstate(), 100) + def test_wrap_lenfunc_bad_cast(self): + try: + large_range = xrange(sys.maxsize) + except OverflowError as exc: + self.skipTest("xrange(sys.maxsize) failed with: %s" % exc) + self.assertEqual(large_range.__len__(), sys.maxsize) + + class ClassPropertiesAndMethods(unittest.TestCase): def assertHasAttr(self, obj, name): diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index 4dff415..c45b892 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -1,5 +1,6 @@ from DocXMLRPCServer import DocXMLRPCServer import httplib +import re import sys from test import test_support threading = test_support.import_module('threading') @@ -176,6 +177,25 @@ class DocXMLRPCHTTPGETServer(unittest.TestCase): self.assertIn("""Try self.add, too.""", response.read()) + def test_server_title_escape(self): + """Test that the server title and documentation + are escaped for HTML. + """ + self.serv.set_server_title('test_title