[Python-checkins] cpython (merge 2.7 -> 2.7): merge heads

benjamin.peterson python-checkins at python.org
Thu Jun 9 02:19:06 EDT 2016


https://hg.python.org/cpython/rev/b72ff4d59f62
changeset:   101818:b72ff4d59f62
branch:      2.7
parent:      101817:0d09fc6a4081
parent:      101794:8700f4d09b28
user:        Benjamin Peterson <benjamin at python.org>
date:        Wed Jun 08 23:18:56 2016 -0700
summary:
  merge heads

files:
  Doc/distributing/index.rst            |    2 +-
  Doc/distutils/index.rst               |   11 +-
  Doc/install/index.rst                 |   11 +-
  Doc/library/basehttpserver.rst        |    5 +-
  Doc/library/distutils.rst             |   40 +-
  Doc/reference/simple_stmts.rst        |    2 +-
  Doc/tools/templates/indexcontent.html |    8 +-
  Lib/BaseHTTPServer.py                 |   21 +-
  Lib/idlelib/NEWS.txt                  |    3 +
  Lib/idlelib/idle_test/__init__.py     |    6 +
  Lib/platform.py                       |   15 +-
  Lib/test/test_httpservers.py          |   38 +
  Lib/test/test_platform.py             |   16 +
  Lib/test/test_turtle.py               |  435 ++++++++++++++
  Mac/BuildScript/build-installer.py    |    6 +-
  Makefile.pre.in                       |    7 +-
  Misc/ACKS                             |    1 +
  Misc/NEWS                             |   28 +-
  Objects/typeobject.c                  |    6 +-
  19 files changed, 616 insertions(+), 45 deletions(-)


diff --git a/Doc/distributing/index.rst b/Doc/distributing/index.rst
--- a/Doc/distributing/index.rst
+++ b/Doc/distributing/index.rst
@@ -61,7 +61,7 @@
   extensions, to be installed on a system without needing to be built
   locally.
 
-.. _setuptools: https://setuptools.pypa.io/en/latest/setuptools.html
+.. _setuptools: https://setuptools.readthedocs.io/en/latest/
 .. _wheel: https://wheel.readthedocs.org
 
 Open source licensing and collaboration
diff --git a/Doc/distutils/index.rst b/Doc/distutils/index.rst
--- a/Doc/distutils/index.rst
+++ b/Doc/distutils/index.rst
@@ -1,12 +1,17 @@
 .. _distutils-index:
 
-###############################
-  Distributing Python Modules
-###############################
+##############################################
+  Distributing Python Modules (Legacy version)
+##############################################
 
 :Authors: Greg Ward, Anthony Baxter
 :Email: distutils-sig at python.org
 
+.. seealso::
+
+   :ref:`distributing-index`
+      The up to date module distribution documentations
+
 This document describes the Python Distribution Utilities ("Distutils") from
 the module developer's point of view, describing how to use the Distutils to
 make Python modules and extensions easily available to a wider audience with
diff --git a/Doc/install/index.rst b/Doc/install/index.rst
--- a/Doc/install/index.rst
+++ b/Doc/install/index.rst
@@ -2,14 +2,19 @@
 
 .. _install-index:
 
-*****************************
-  Installing Python Modules
-*****************************
+********************************************
+  Installing Python Modules (Legacy version)
+********************************************
 
 :Author: Greg Ward
 
 .. TODO: Fill in XXX comments
 
+.. seealso::
+
+   :ref:`installing-index`
+      The up to date module installation documentations
+
 .. The audience for this document includes people who don't know anything
    about Python and aren't about to learn the language just in order to
    install and maintain it for their users, i.e. system administrators.
diff --git a/Doc/library/basehttpserver.rst b/Doc/library/basehttpserver.rst
--- a/Doc/library/basehttpserver.rst
+++ b/Doc/library/basehttpserver.rst
@@ -197,7 +197,10 @@
       Sends and logs a complete error reply to the client. The numeric *code*
       specifies the HTTP error code, with *message* as optional, more specific text. A
       complete set of headers is sent, followed by text composed using the
-      :attr:`error_message_format` class variable.
+      :attr:`error_message_format` class variable. The body will be empty
+      if the method is HEAD or the response code is one of the following:
+      ``1xx``, ``204 No Content``, ``205 Reset Content``,
+      ``304 Not Modified``.
 
 
    .. method:: send_response(code[, message])
diff --git a/Doc/library/distutils.rst b/Doc/library/distutils.rst
--- a/Doc/library/distutils.rst
+++ b/Doc/library/distutils.rst
@@ -1,10 +1,9 @@
-
 :mod:`distutils` --- Building and installing Python modules
 ===========================================================
 
 .. module:: distutils
-   :synopsis: Support for building and installing Python modules into an existing Python
-              installation.
+   :synopsis: Support for building and installing Python modules into an
+              existing Python installation.
 .. sectionauthor:: Fred L. Drake, Jr. <fdrake at acm.org>
 
 
@@ -13,18 +12,31 @@
 100%-pure Python, or may be extension modules written in C, or may be
 collections of Python packages which include modules coded in both Python and C.
 
-This package is discussed in two separate chapters:
+Most Python users will *not* want to use this module directly, but instead
+use the cross-version tools maintained by the Python Packaging Authority. In
+particular,
+`setuptools <https://setuptools.readthedocs.io/en/latest/>`__ is an
+enhanced alternative to :mod:`distutils` that provides:
 
+* support for declaring project dependencies
+* additional mechanisms for configuring which files to include in source
+  releases (including plugins for integration with version control systems)
+* the ability to declare project "entry points", which can be used as the
+  basis for application plugin systems
+* the ability to automatically generate Windows command line executables at
+  installation time rather than needing to prebuild them
+* consistent behaviour across all supported Python versions
 
-.. seealso::
+The recommended `pip <https://pip.pypa.io/>`__ installer runs all
+``setup.py`` scripts with ``setuptools``, even if the script itself only
+imports ``distutils``. Refer to the
+`Python Packaging User Guide <https://packaging.python.org>`_ for more
+information.
 
-   :ref:`distutils-index`
-      The manual for developers and packagers of Python modules.  This describes how
-      to prepare :mod:`distutils`\ -based packages so that they may be easily
-      installed into an existing Python installation.
+For the benefits of packaging tool authors and users seeking a deeper
+understanding of the details of the current packaging and distribution
+system, the legacy :mod:`distutils` based user documentation and API
+reference remain available:
 
-   :ref:`install-index`
-      An "administrators" manual which includes information on installing modules into
-      an existing Python installation. You do not need to be a Python programmer to
-      read this manual.
-
+* :ref:`install-index`
+* :ref:`distutils-index`
diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst
--- a/Doc/reference/simple_stmts.rst
+++ b/Doc/reference/simple_stmts.rst
@@ -87,7 +87,7 @@
    target_list: `target` ("," `target`)* [","]
    target: `identifier`
          : | "(" `target_list` ")"
-         : | "[" `target_list` "]"
+         : | "[" [`target_list`] "]"
          : | `attributeref`
          : | `subscription`
          : | `slicing`
diff --git a/Doc/tools/templates/indexcontent.html b/Doc/tools/templates/indexcontent.html
--- a/Doc/tools/templates/indexcontent.html
+++ b/Doc/tools/templates/indexcontent.html
@@ -16,14 +16,14 @@
       <p class="biglink"><a class="biglink" href="{{ pathto("howto/index") }}">{% trans %}Python HOWTOs{% endtrans %}</a><br/>
          <span class="linkdescr">{% trans %}in-depth documents on specific topics{% endtrans %}</span></p>
     </td><td width="50%">
+      <p class="biglink"><a class="biglink" href="{{ pathto("installing/index") }}">{% trans %}Installing Python Modules{% endtrans %}</a><br/>
+         <span class="linkdescr">{% trans %}installing from the Python Package Index & other sources{% endtrans %}</span></p>
+      <p class="biglink"><a class="biglink" href="{{ pathto("distributing/index") }}">{% trans %}Distributing Python Modules{% endtrans %}</a><br/>
+         <span class="linkdescr">{% trans %}publishing modules for installation by others{% endtrans %}</span></p>
       <p class="biglink"><a class="biglink" href="{{ pathto("extending/index") }}">{% trans %}Extending and Embedding{% endtrans %}</a><br/>
          <span class="linkdescr">{% trans %}tutorial for C/C++ programmers{% endtrans %}</span></p>
       <p class="biglink"><a class="biglink" href="{{ pathto("c-api/index") }}">{% trans %}Python/C API{% endtrans %}</a><br/>
          <span class="linkdescr">{% trans %}reference for C/C++ programmers{% endtrans %}</span></p>
-      <p class="biglink"><a class="biglink" href="{{ pathto("install/index") }}">{% trans %}Installing Python Modules{% endtrans %}</a><br/>
-         <span class="linkdescr">{% trans %}information for installers & sys-admins{% endtrans %}</span></p>
-      <p class="biglink"><a class="biglink" href="{{ pathto("distutils/index") }}">{% trans %}Distributing Python Modules{% endtrans %}</a><br/>
-         <span class="linkdescr">{% trans %}sharing modules with others{% endtrans %}</span></p>
       <p class="biglink"><a class="biglink" href="{{ pathto("faq/index") }}">{% trans %}FAQs{% endtrans %}</a><br/>
          <span class="linkdescr">{% trans %}frequently asked questions (with answers!){% endtrans %}</span></p>
     </td></tr>
diff --git a/Lib/BaseHTTPServer.py b/Lib/BaseHTTPServer.py
--- a/Lib/BaseHTTPServer.py
+++ b/Lib/BaseHTTPServer.py
@@ -362,14 +362,25 @@
             message = short
         explain = long
         self.log_error("code %d, message %s", code, message)
-        # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201)
-        content = (self.error_message_format %
-                   {'code': code, 'message': _quote_html(message), 'explain': explain})
         self.send_response(code, message)
-        self.send_header("Content-Type", self.error_content_type)
         self.send_header('Connection', 'close')
+
+        # Message body is omitted for cases described in:
+        #  - RFC7230: 3.3. 1xx, 204(No Content), 304(Not Modified)
+        #  - RFC7231: 6.3.6. 205(Reset Content)
+        content = None
+        if code >= 200 and code not in (204, 205, 304):
+            # HTML encode to prevent Cross Site Scripting attacks
+            # (see bug #1100201)
+            content = (self.error_message_format % {
+                'code': code,
+                'message': _quote_html(message),
+                'explain': explain
+            })
+            self.send_header("Content-Type", self.error_content_type)
         self.end_headers()
-        if self.command != 'HEAD' and code >= 200 and code not in (204, 304):
+
+        if self.command != 'HEAD' and content:
             self.wfile.write(content)
 
     error_message_format = DEFAULT_ERROR_MESSAGE
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -2,6 +2,9 @@
 ==========================
 *Release date: 2015-06-30?*
 
+- Issue #24759: Make clear in idlelib.idle_test.__init__ that the directory
+  is a private implementation of test.test_idle and tool for maintainers.
+
 - Issue #26673: When tk reports font size as 0, change to size 10.
   Such fonts on Linux prevented the configuration dialog from opening.
 
diff --git a/Lib/idlelib/idle_test/__init__.py b/Lib/idlelib/idle_test/__init__.py
--- a/Lib/idlelib/idle_test/__init__.py
+++ b/Lib/idlelib/idle_test/__init__.py
@@ -1,3 +1,9 @@
+'''idlelib.idle_test is a private implementation of test.test_idle,
+which tests the IDLE application as part of the stdlib test suite.
+Run IDLE tests alone with "python -m test.test_idle".
+This package and its contained modules are subject to change and
+any direct use is at your own risk.
+'''
 from os.path import dirname
 
 def load_tests(loader, standard_tests, pattern):
diff --git a/Lib/platform.py b/Lib/platform.py
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -1314,9 +1314,11 @@
 ### Various APIs for extracting information from sys.version
 
 _sys_version_parser = re.compile(
-    r'([\w.+]+)\s*'
-    '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'
-    '\[([^\]]+)\]?')
+    r'([\w.+]+)\s*'  # "version<space>"
+    r'\(#?([^,]+)'  # "(#buildno"
+    r'(?:,\s*([\w ]*)'  # ", builddate"
+    r'(?:,\s*([\w :]*))?)?\)\s*'  # ", buildtime)<space>"
+    r'\[([^\]]+)\]?')  # "[compiler]"
 
 _ironpython_sys_version_parser = re.compile(
     r'IronPython\s*'
@@ -1395,6 +1397,8 @@
                 'failed to parse Jython sys.version: %s' %
                 repr(sys_version))
         version, buildno, builddate, buildtime, _ = match.groups()
+        if builddate is None:
+            builddate = ''
         compiler = sys.platform
 
     elif "PyPy" in sys_version:
@@ -1417,7 +1421,10 @@
         version, buildno, builddate, buildtime, compiler = \
               match.groups()
         name = 'CPython'
-        builddate = builddate + ' ' + buildtime
+        if builddate is None:
+            builddate = ''
+        elif buildtime:
+            builddate = builddate + ' ' + buildtime
 
     if hasattr(sys, 'subversion'):
         # sys.subversion was added in Python 2.5
diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py
--- a/Lib/test/test_httpservers.py
+++ b/Lib/test/test_httpservers.py
@@ -178,6 +178,12 @@
             self.send_header('Connection', 'close')
             self.end_headers()
 
+        def do_SEND_ERROR(self):
+            self.send_error(int(self.path[1:]))
+
+        def do_HEAD(self):
+            self.send_error(int(self.path[1:]))
+
     def setUp(self):
         BaseTestCase.setUp(self)
         self.con = httplib.HTTPConnection('localhost', self.PORT)
@@ -276,6 +282,38 @@
         res = self.con.getresponse()
         self.assertEqual(res.status, 999)
 
+    def test_send_error(self):
+        allow_transfer_encoding_codes = (205, 304)
+        for code in (101, 102, 204, 205, 304):
+            self.con.request('SEND_ERROR', '/{}'.format(code))
+            res = self.con.getresponse()
+            self.assertEqual(code, res.status)
+            self.assertEqual(None, res.getheader('Content-Length'))
+            self.assertEqual(None, res.getheader('Content-Type'))
+            if code not in allow_transfer_encoding_codes:
+                self.assertEqual(None, res.getheader('Transfer-Encoding'))
+
+            data = res.read()
+            self.assertEqual(b'', data)
+
+    def test_head_via_send_error(self):
+        allow_transfer_encoding_codes = (205, 304)
+        for code in (101, 200, 204, 205, 304):
+            self.con.request('HEAD', '/{}'.format(code))
+            res = self.con.getresponse()
+            self.assertEqual(code, res.status)
+            if code == 200:
+                self.assertEqual(None, res.getheader('Content-Length'))
+                self.assertIn('text/html', res.getheader('Content-Type'))
+            else:
+                self.assertEqual(None, res.getheader('Content-Length'))
+                self.assertEqual(None, res.getheader('Content-Type'))
+            if code not in allow_transfer_encoding_codes:
+                self.assertEqual(None, res.getheader('Transfer-Encoding'))
+
+            data = res.read()
+            self.assertEqual(b'', data)
+
 
 class SimpleHTTPServerTestCase(BaseTestCase):
     class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler):
diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py
--- a/Lib/test/test_platform.py
+++ b/Lib/test/test_platform.py
@@ -67,6 +67,22 @@
              ('IronPython', '1.0.60816', '', '', '', '', '.NET 2.0.50727.42')),
             ('IronPython 1.0 (1.0.61005.1977) on .NET 2.0.50727.42',
              ('IronPython', '1.0.0', '', '', '', '', '.NET 2.0.50727.42')),
+            ('2.4.3 (truncation, date, t) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', 'date t', 'GCC')),
+            ('2.4.3 (truncation, date, ) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
+            ('2.4.3 (truncation, date,) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
+            ('2.4.3 (truncation, date) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', 'date', 'GCC')),
+            ('2.4.3 (truncation, d) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', 'd', 'GCC')),
+            ('2.4.3 (truncation, ) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
+            ('2.4.3 (truncation,) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
+            ('2.4.3 (truncation) \n[GCC]',
+             ('CPython', '2.4.3', '', '', 'truncation', '', 'GCC')),
             ):
             # branch and revision are not "parsed", but fetched
             # from sys.subversion.  Ignore them
diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/test_turtle.py
@@ -0,0 +1,435 @@
+import pickle
+import unittest
+from test import test_support as support
+
+turtle = support.import_module('turtle')
+Vec2D = turtle.Vec2D
+
+test_config = """\
+width = 0.75
+height = 0.8
+canvwidth = 500
+canvheight = 200
+leftright = 100
+topbottom = 100
+mode = world
+colormode = 255
+delay = 100
+undobuffersize = 10000
+shape = circle
+pencolor  = red
+fillcolor  = blue
+resizemode  = auto
+visible  = None
+language = english
+exampleturtle = turtle
+examplescreen = screen
+title = Python Turtle Graphics
+using_IDLE = ''
+"""
+
+test_config_two = """\
+# Comments!
+# Testing comments!
+pencolor  = red
+fillcolor  = blue
+visible  = False
+language = english
+# Some more
+# comments
+using_IDLE = False
+"""
+
+invalid_test_config = """
+pencolor = red
+fillcolor: blue
+visible = False
+"""
+
+
+class TurtleConfigTest(unittest.TestCase):
+
+    def get_cfg_file(self, cfg_str):
+        self.addCleanup(support.unlink, support.TESTFN)
+        with open(support.TESTFN, 'w') as f:
+            f.write(cfg_str)
+        return support.TESTFN
+
+    def test_config_dict(self):
+
+        cfg_name = self.get_cfg_file(test_config)
+        parsed_cfg = turtle.config_dict(cfg_name)
+
+        expected = {
+            'width' : 0.75,
+            'height' : 0.8,
+            'canvwidth' : 500,
+            'canvheight': 200,
+            'leftright': 100,
+            'topbottom': 100,
+            'mode': 'world',
+            'colormode': 255,
+            'delay': 100,
+            'undobuffersize': 10000,
+            'shape': 'circle',
+            'pencolor' : 'red',
+            'fillcolor' : 'blue',
+            'resizemode' : 'auto',
+            'visible' : None,
+            'language': 'english',
+            'exampleturtle': 'turtle',
+            'examplescreen': 'screen',
+            'title': 'Python Turtle Graphics',
+            'using_IDLE': '',
+        }
+
+        self.assertEqual(parsed_cfg, expected)
+
+    def test_partial_config_dict_with_commments(self):
+
+        cfg_name = self.get_cfg_file(test_config_two)
+        parsed_cfg = turtle.config_dict(cfg_name)
+
+        expected = {
+            'pencolor': 'red',
+            'fillcolor': 'blue',
+            'visible': False,
+            'language': 'english',
+            'using_IDLE': False,
+        }
+
+        self.assertEqual(parsed_cfg, expected)
+
+    def test_config_dict_invalid(self):
+
+        cfg_name = self.get_cfg_file(invalid_test_config)
+
+        with support.captured_stdout() as stdout:
+            parsed_cfg = turtle.config_dict(cfg_name)
+
+        err_msg = stdout.getvalue()
+
+        self.assertIn('Bad line in config-file ', err_msg)
+        self.assertIn('fillcolor: blue', err_msg)
+
+        self.assertEqual(parsed_cfg, {
+            'pencolor': 'red',
+            'visible': False,
+        })
+
+
+class VectorComparisonMixin:
+
+    def assertVectorsAlmostEqual(self, vec1, vec2):
+        if len(vec1) != len(vec2):
+            self.fail("Tuples are not of equal size")
+        for idx, (i, j) in enumerate(zip(vec1, vec2)):
+            self.assertAlmostEqual(
+                i, j, msg='values at index {} do not match'.format(idx))
+
+
+class TestVec2D(VectorComparisonMixin, unittest.TestCase):
+
+    def test_constructor(self):
+        vec = Vec2D(0.5, 2)
+        self.assertEqual(vec[0], 0.5)
+        self.assertEqual(vec[1], 2)
+        self.assertIsInstance(vec, Vec2D)
+
+        self.assertRaises(TypeError, Vec2D)
+        self.assertRaises(TypeError, Vec2D, 0)
+        self.assertRaises(TypeError, Vec2D, (0, 1))
+        self.assertRaises(TypeError, Vec2D, vec)
+        self.assertRaises(TypeError, Vec2D, 0, 1, 2)
+
+    def test_repr(self):
+        vec = Vec2D(0.567, 1.234)
+        self.assertEqual(repr(vec), '(0.57,1.23)')
+
+    def test_equality(self):
+        vec1 = Vec2D(0, 1)
+        vec2 = Vec2D(0.0, 1)
+        vec3 = Vec2D(42, 1)
+        self.assertEqual(vec1, vec2)
+        self.assertEqual(vec1, tuple(vec1))
+        self.assertEqual(tuple(vec1), vec1)
+        self.assertNotEqual(vec1, vec3)
+        self.assertNotEqual(vec2, vec3)
+
+    def test_pickling(self):
+        vec = Vec2D(0.5, 2)
+        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+            pickled = pickle.dumps(vec, protocol=proto)
+            unpickled = pickle.loads(pickled)
+            self.assertEqual(unpickled, vec)
+            self.assertIsInstance(unpickled, Vec2D)
+
+    def _assert_arithmetic_cases(self, test_cases, lambda_operator):
+        for test_case in test_cases:
+            ((first, second), expected) = test_case
+
+            op1 = Vec2D(*first)
+            op2 = Vec2D(*second)
+
+            result = lambda_operator(op1, op2)
+
+            expected = Vec2D(*expected)
+
+            self.assertVectorsAlmostEqual(result, expected)
+
+    def test_vector_addition(self):
+
+        test_cases = [
+            (((0, 0), (1, 1)), (1.0, 1.0)),
+            (((-1, 0), (2, 2)), (1, 2)),
+            (((1.5, 0), (1, 1)), (2.5, 1)),
+        ]
+
+        self._assert_arithmetic_cases(test_cases, lambda x, y: x + y)
+
+    def test_vector_subtraction(self):
+
+        test_cases = [
+            (((0, 0), (1, 1)), (-1, -1)),
+            (((10.625, 0.125), (10, 0)), (0.625, 0.125)),
+        ]
+
+        self._assert_arithmetic_cases(test_cases, lambda x, y: x - y)
+
+    def test_vector_multiply(self):
+
+        vec1 = Vec2D(10, 10)
+        vec2 = Vec2D(0.5, 3)
+        answer = vec1 * vec2
+        expected = 35
+        self.assertAlmostEqual(answer, expected)
+
+        vec = Vec2D(0.5, 3)
+        answer = vec * 10
+        expected = Vec2D(5, 30)
+        self.assertVectorsAlmostEqual(answer, expected)
+
+    def test_vector_negative(self):
+        vec = Vec2D(10, -10)
+        expected = (-10, 10)
+        self.assertVectorsAlmostEqual(-vec, expected)
+
+    def test_distance(self):
+        vec = Vec2D(6, 8)
+        expected = 10
+        self.assertEqual(abs(vec), expected)
+
+        vec = Vec2D(0, 0)
+        expected = 0
+        self.assertEqual(abs(vec), expected)
+
+        vec = Vec2D(2.5, 6)
+        expected = 6.5
+        self.assertEqual(abs(vec), expected)
+
+    def test_rotate(self):
+
+        cases = [
+            (((0, 0), 0), (0, 0)),
+            (((0, 1), 90), (-1, 0)),
+            (((0, 1), -90), (1, 0)),
+            (((1, 0), 180), (-1, 0)),
+            (((1, 0), 360), (1, 0)),
+        ]
+
+        for case in cases:
+            (vec, rot), expected = case
+            vec = Vec2D(*vec)
+            got = vec.rotate(rot)
+            self.assertVectorsAlmostEqual(got, expected)
+
+
+class TestTNavigator(VectorComparisonMixin, unittest.TestCase):
+
+    def setUp(self):
+        self.nav = turtle.TNavigator()
+
+    def test_goto(self):
+        self.nav.goto(100, -100)
+        self.assertAlmostEqual(self.nav.xcor(), 100)
+        self.assertAlmostEqual(self.nav.ycor(), -100)
+
+    def test_pos(self):
+        self.assertEqual(self.nav.pos(), self.nav._position)
+        self.nav.goto(100, -100)
+        self.assertEqual(self.nav.pos(), self.nav._position)
+
+    def test_left(self):
+        self.assertEqual(self.nav._orient, (1.0, 0))
+        self.nav.left(90)
+        self.assertVectorsAlmostEqual(self.nav._orient, (0.0, 1.0))
+
+    def test_right(self):
+        self.assertEqual(self.nav._orient, (1.0, 0))
+        self.nav.right(90)
+        self.assertVectorsAlmostEqual(self.nav._orient, (0, -1.0))
+
+    def test_reset(self):
+        self.nav.goto(100, -100)
+        self.assertAlmostEqual(self.nav.xcor(), 100)
+        self.assertAlmostEqual(self.nav.ycor(), -100)
+        self.nav.reset()
+        self.assertAlmostEqual(self.nav.xcor(), 0)
+        self.assertAlmostEqual(self.nav.ycor(), 0)
+
+    def test_forward(self):
+        self.nav.forward(150)
+        expected = Vec2D(150, 0)
+        self.assertVectorsAlmostEqual(self.nav.position(), expected)
+
+        self.nav.reset()
+        self.nav.left(90)
+        self.nav.forward(150)
+        expected = Vec2D(0, 150)
+        self.assertVectorsAlmostEqual(self.nav.position(), expected)
+
+        self.assertRaises(TypeError, self.nav.forward, 'skldjfldsk')
+
+    def test_backwards(self):
+        self.nav.back(200)
+        expected = Vec2D(-200, 0)
+        self.assertVectorsAlmostEqual(self.nav.position(), expected)
+
+        self.nav.reset()
+        self.nav.right(90)
+        self.nav.back(200)
+        expected = Vec2D(0, 200)
+        self.assertVectorsAlmostEqual(self.nav.position(), expected)
+
+    def test_distance(self):
+        self.nav.forward(100)
+        expected = 100
+        self.assertAlmostEqual(self.nav.distance(Vec2D(0,0)), expected)
+
+    def test_radians_and_degrees(self):
+        self.nav.left(90)
+        self.assertAlmostEqual(self.nav.heading(), 90)
+        self.nav.radians()
+        self.assertAlmostEqual(self.nav.heading(), 1.57079633)
+        self.nav.degrees()
+        self.assertAlmostEqual(self.nav.heading(), 90)
+
+    def test_towards(self):
+
+        coordinates = [
+            # coordinates, expected
+            ((100, 0), 0.0),
+            ((100, 100), 45.0),
+            ((0, 100), 90.0),
+            ((-100, 100), 135.0),
+            ((-100, 0), 180.0),
+            ((-100, -100), 225.0),
+            ((0, -100), 270.0),
+            ((100, -100), 315.0),
+        ]
+
+        for (x, y), expected in coordinates:
+            self.assertEqual(self.nav.towards(x, y), expected)
+            self.assertEqual(self.nav.towards((x, y)), expected)
+            self.assertEqual(self.nav.towards(Vec2D(x, y)), expected)
+
+    def test_heading(self):
+
+        self.nav.left(90)
+        self.assertAlmostEqual(self.nav.heading(), 90)
+        self.nav.left(45)
+        self.assertAlmostEqual(self.nav.heading(), 135)
+        self.nav.right(1.6)
+        self.assertAlmostEqual(self.nav.heading(), 133.4)
+        self.assertRaises(TypeError, self.nav.right, 'sdkfjdsf')
+        self.nav.reset()
+
+        rotations = [10, 20, 170, 300]
+        result = sum(rotations) % 360
+        for num in rotations:
+            self.nav.left(num)
+        self.assertEqual(self.nav.heading(), result)
+        self.nav.reset()
+
+        result = (360-sum(rotations)) % 360
+        for num in rotations:
+            self.nav.right(num)
+        self.assertEqual(self.nav.heading(), result)
+        self.nav.reset()
+
+        rotations = [10, 20, -170, 300, -210, 34.3, -50.2, -10, -29.98, 500]
+        sum_so_far = 0
+        for num in rotations:
+            if num < 0:
+                self.nav.right(abs(num))
+            else:
+                self.nav.left(num)
+            sum_so_far += num
+            self.assertAlmostEqual(self.nav.heading(), sum_so_far % 360)
+
+    def test_setheading(self):
+        self.nav.setheading(102.32)
+        self.assertAlmostEqual(self.nav.heading(), 102.32)
+        self.nav.setheading(-123.23)
+        self.assertAlmostEqual(self.nav.heading(), (-123.23) % 360)
+        self.nav.setheading(-1000.34)
+        self.assertAlmostEqual(self.nav.heading(), (-1000.34) % 360)
+        self.nav.setheading(300000)
+        self.assertAlmostEqual(self.nav.heading(), 300000%360)
+
+    def test_positions(self):
+        self.nav.forward(100)
+        self.nav.left(90)
+        self.nav.forward(-200)
+        self.assertVectorsAlmostEqual(self.nav.pos(), (100.0, -200.0))
+
+    def test_setx_and_sety(self):
+        self.nav.setx(-1023.2334)
+        self.nav.sety(193323.234)
+        self.assertVectorsAlmostEqual(self.nav.pos(), (-1023.2334, 193323.234))
+
+    def test_home(self):
+        self.nav.left(30)
+        self.nav.forward(-100000)
+        self.nav.home()
+        self.assertVectorsAlmostEqual(self.nav.pos(), (0,0))
+        self.assertAlmostEqual(self.nav.heading(), 0)
+
+    def test_distance_method(self):
+        self.assertAlmostEqual(self.nav.distance(30, 40), 50)
+        vec = Vec2D(0.22, .001)
+        self.assertAlmostEqual(self.nav.distance(vec), 0.22000227271553355)
+        another_turtle = turtle.TNavigator()
+        another_turtle.left(90)
+        another_turtle.forward(10000)
+        self.assertAlmostEqual(self.nav.distance(another_turtle), 10000)
+
+
+class TestTPen(unittest.TestCase):
+
+    def test_pendown_and_penup(self):
+
+        tpen = turtle.TPen()
+
+        self.assertTrue(tpen.isdown())
+        tpen.penup()
+        self.assertFalse(tpen.isdown())
+        tpen.pendown()
+        self.assertTrue(tpen.isdown())
+
+    def test_showturtle_hideturtle_and_isvisible(self):
+
+        tpen = turtle.TPen()
+
+        self.assertTrue(tpen.isvisible())
+        tpen.hideturtle()
+        self.assertFalse(tpen.isvisible())
+        tpen.showturtle()
+        self.assertTrue(tpen.isvisible())
+
+
+def test_main():
+    support.run_unittest(TurtleConfigTest, TestVec2D, TestTNavigator, TestTPen)
+
+if __name__ == '__main__':
+    test_main()
diff --git a/Mac/BuildScript/build-installer.py b/Mac/BuildScript/build-installer.py
--- a/Mac/BuildScript/build-installer.py
+++ b/Mac/BuildScript/build-installer.py
@@ -242,9 +242,9 @@
 
         result.extend([
           dict(
-              name="OpenSSL 1.0.2g",
-              url="https://www.openssl.org/source/openssl-1.0.2g.tar.gz",
-              checksum='f3c710c045cdee5fd114feb69feba7aa',
+              name="OpenSSL 1.0.2h",
+              url="https://www.openssl.org/source/openssl-1.0.2h.tar.gz",
+              checksum='9392e65072ce4b614c1392eefc1f23d0',
               patches=[
                   "openssl_sdk_makedepend.patch",
                    ],
diff --git a/Makefile.pre.in b/Makefile.pre.in
--- a/Makefile.pre.in
+++ b/Makefile.pre.in
@@ -682,16 +682,19 @@
 
 $(GRAMMAR_H): $(GRAMMAR_INPUT) $(PGEN)
 	@$(MKDIR_P) Include
+	# Avoid copying the file onto itself for an in-tree build
 	if test "$(cross_compiling)" != "yes"; then \
 		$(PGEN) $(GRAMMAR_INPUT) $(GRAMMAR_H) $(GRAMMAR_C); \
 	else \
-		cp $(srcdir)/Include/graminit.h $(GRAMMAR_H); \
+		cp $(srcdir)/Include/graminit.h $(GRAMMAR_H).tmp; \
+		mv $(GRAMMAR_H).tmp $(GRAMMAR_H); \
 	fi
 $(GRAMMAR_C): $(GRAMMAR_H)
 	if test "$(cross_compiling)" != "yes"; then \
 		touch $(GRAMMAR_C); \
 	else \
-		cp $(srcdir)/Python/graminit.c $(GRAMMAR_C); \
+		cp $(srcdir)/Python/graminit.c $(GRAMMAR_C).tmp; \
+		mv $(GRAMMAR_C).tmp $(GRAMMAR_C); \
 	fi
 
 $(PGEN):	$(PGENOBJS)
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -741,6 +741,7 @@
 Марк Коренберг
 Vlad Korolev
 Anna Koroliuk
+Susumu Koshiba
 Joseph Koshy
 Daniel Kozan
 Jerzy Kozera
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,7 +18,7 @@
   Some compilers and toolchains are known to not produce stable code when
   using LTO, be sure to test things thoroughly before relying on it.
   It can provide a few % speed up over profile-opt alone.
-  
+
 - Issue #26168: Fixed possible refleaks in failing Py_BuildValue() with the "N"
   format unit.
 
@@ -92,6 +92,14 @@
 Library
 -------
 
+- Issue #25738: Stop BaseHTTPServer.BaseHTTPRequestHandler.send_error() from
+  sending a message body for 205 Reset Content.  Also, don't send the
+  Content-Type header field in responses that don't have a body.  Based on
+  patch by Susumu Koshiba.
+
+- Issue #21313: Fix the "platform" module to tolerate when sys.version
+  contains truncated build information.
+
 - Issue #27211: Fix possible memory corruption in io.IOBase.readline().
 
 - Issue #27114: Fix SSLContext._load_windows_store_certs fails with
@@ -263,6 +271,8 @@
 
 IDLE
 ----
+- Issue #24759: Make clear in idlelib.idle_test.__init__ that the directory
+  is a private implementation of test.test_idle and tool for maintainers.
 
 - Issue #26673: When tk reports font size as 0, change to size 10.
   Such fonts on Linux prevented the configuration dialog from opening.
@@ -293,9 +303,18 @@
   functions together, and add more details such as what underlying Readline
   functions and variables are accessed.
 
+- Issue #26014: Guide users to the newer packaging documentation as was done
+  for Python 3.x.  In particular, the top-level 2.7 documentation page now
+  links to the newer installer and distributions pages rather than the
+  legacy install and Distutils pages; these are still linked to in the
+  library/distutils doc page.
+
 Tests
 -----
 
+- Issue #21916: Added tests for the turtle module.  Patch by ingrid,
+  Gregory Loyse and Jelle Zijlstra.
+
 - Issue #25940: Changed test_ssl to use self-signed.pythontest.net.  This
   avoids relying on svn.python.org, which recently changed root certificate.
 
@@ -312,6 +331,9 @@
 
 - Issue #19450: Update Windows builds to use SQLite 3.8.11.0.
 
+- Issue #27229: Fix the cross-compiling pgen rule for in-tree builds.  Patch
+  by Xavier de Gaye.
+
 - Issue #17603: Avoid error about nonexistant fileblocks.o file by using a
   lower-level check for st_blocks in struct stat.
 
@@ -333,8 +355,8 @@
 
 - Issue #25696: Fix installation of Python on UNIX with make -j9.
 
-- Issue #26465: Update OS X 10.5+ 32-bit-only installer to build
-  and link with OpenSSL 1.0.2g.
+- Issue #26930: Update OS X 10.5+ 32-bit-only installer to build
+  and link with OpenSSL 1.0.2h.
 
 - Issue #26268: Update Windows builds to use OpenSSL 1.0.2f.
 
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -2430,7 +2430,11 @@
             Py_DECREF(type);
             return NULL;
         }
-        PyDict_SetItemString(dict, "__new__", tmp);
+        if (PyDict_SetItemString(dict, "__new__", tmp) < 0) {
+            Py_DECREF(tmp);
+            Py_DECREF(type);
+            return NULL;
+        }
         Py_DECREF(tmp);
     }
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list