branch merge?
diff --git a/.hgtags b/.hgtags
index 407bbcb..c12bf22 100644
--- a/.hgtags
+++ b/.hgtags
@@ -76,6 +76,7 @@
 d18e9d71f369d8211f6ac87252c6d3211f9bd09f v3.1.3rc1
 a4f75773c0060cee38b0bb651a7aba6f56b0e996 v3.1.3
 32fcb9e94985cb19ce37ba9543f091c0dbe9d7dd v3.1.4rc1
+c918ec9f3a76d6afedfbb5d455004de880443a3d v3.1.4
 b37b7834757492d009b99cf0ca4d42d2153d7fac v3.2a1
 56d4373cecb73c8b45126ba7b045b3c7b3f94b0b v3.2a2
 da012d9a2c23d144e399d2e01a55b8a83ad94573 v3.2a3
diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst
index 83b98f9..843707d 100644
--- a/Doc/c-api/intro.rst
+++ b/Doc/c-api/intro.rst
@@ -588,8 +588,8 @@
 
 Compiling the interpreter with the :c:macro:`Py_DEBUG` macro defined produces
 what is generally meant by "a debug build" of Python. :c:macro:`Py_DEBUG` is
-enabled in the Unix build by adding :option:`--with-pydebug` to the
-:file:`configure` command.  It is also implied by the presence of the
+enabled in the Unix build by adding ``--with-pydebug`` to the
+:file:`./configure` command.  It is also implied by the presence of the
 not-Python-specific :c:macro:`_DEBUG` macro.  When :c:macro:`Py_DEBUG` is enabled
 in the Unix build, compiler optimization is disabled.
 
diff --git a/Doc/distutils/introduction.rst b/Doc/distutils/introduction.rst
index 8dc604d..57d34a4 100644
--- a/Doc/distutils/introduction.rst
+++ b/Doc/distutils/introduction.rst
@@ -79,11 +79,17 @@
   for an example)
 
 To create a source distribution for this module, you would create a setup
-script, :file:`setup.py`, containing the above code, and run::
+script, :file:`setup.py`, containing the above code, and run this command from a
+terminal::
 
    python setup.py sdist
 
-which will create an archive file (e.g., tarball on Unix, ZIP file on Windows)
+For Windows, open a command prompt windows ("DOS box") and change the command
+to::
+
+   setup.py sdist
+
+:command:`sdist` will create an archive file (e.g., tarball on Unix, ZIP file on Windows)
 containing your setup script :file:`setup.py`, and your module :file:`foo.py`.
 The archive file will be named :file:`foo-1.0.tar.gz` (or :file:`.zip`), and
 will unpack into a directory :file:`foo-1.0`.
diff --git a/Doc/documenting/markup.rst b/Doc/documenting/markup.rst
index 57d9eeb..c005d0c 100644
--- a/Doc/documenting/markup.rst
+++ b/Doc/documenting/markup.rst
@@ -98,11 +98,12 @@
 
        Spam or ham the foo.
 
-The signatures of object methods or data attributes should always include the
-type name (``.. method:: FileInput.input(...)``), even if it is obvious from the
-context which type they belong to; this is to enable consistent
-cross-references.  If you describe methods belonging to an abstract protocol,
-such as "context managers", include a (pseudo-)type name too to make the
+The signatures of object methods or data attributes should not include the
+class name, but be nested in a class directive.  The generated files will
+reflect this nesting, and the target identifiers (for HTML output) will use
+both the class and method name, to enable consistent cross-references.  If you
+describe methods belonging to an abstract protocol such as context managers,
+use a class directive with a (pseudo-)type name too to make the
 index entries more informative.
 
 The directives are:
diff --git a/Doc/install/index.rst b/Doc/install/index.rst
index 31c1d7f..f8d6305 100644
--- a/Doc/install/index.rst
+++ b/Doc/install/index.rst
@@ -96,10 +96,16 @@
 directory: :file:`foo-1.0` or :file:`widget-0.9.7`.  Additionally, the
 distribution will contain a setup script :file:`setup.py`, and a file named
 :file:`README.txt` or possibly just :file:`README`, which should explain that
-building and installing the module distribution is a simple matter of running ::
+building and installing the module distribution is a simple matter of running
+one command from a terminal::
 
    python setup.py install
 
+For Windows, this command should be run from a command prompt windows ("DOS
+box")::
+
+   setup.py install
+
 If all these things are true, then you already know how to build and install the
 modules you've just downloaded:  Run the command above. Unless you need to
 install things in a non-standard way or customize the build process, you don't
@@ -113,14 +119,11 @@
 ==========================
 
 As described in section :ref:`inst-new-standard`, building and installing a module
-distribution using the Distutils is usually one simple command::
+distribution using the Distutils is usually one simple command to run from a
+terminal::
 
    python setup.py install
 
-On Unix, you'd run this command from a shell prompt; on Windows, you have to
-open a command prompt window ("DOS box") and do it there; on Mac OS X, you open
-a :command:`Terminal` window to get a shell prompt.
-
 
 .. _inst-platform-variations:
 
diff --git a/Doc/library/builtins.rst b/Doc/library/builtins.rst
index c495728..d4199d2 100644
--- a/Doc/library/builtins.rst
+++ b/Doc/library/builtins.rst
@@ -7,7 +7,9 @@
 
 This module provides direct access to all 'built-in' identifiers of Python; for
 example, ``builtins.open`` is the full name for the built-in function
-:func:`open`.
+:func:`open`.  See :ref:`built-in-funcs` and :ref:`built-in-consts` for
+documentation.
+
 
 This module is not normally accessed explicitly by most applications, but can be
 useful in modules that provide objects with the same name as a built-in value,
diff --git a/Doc/library/constants.rst b/Doc/library/constants.rst
index 51a1c26..fa61f68 100644
--- a/Doc/library/constants.rst
+++ b/Doc/library/constants.rst
@@ -1,3 +1,5 @@
+.. _built-in-consts:
+
 Built-in Constants
 ==================
 
diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst
index eda9302..378c071 100644
--- a/Doc/library/logging.handlers.rst
+++ b/Doc/library/logging.handlers.rst
@@ -436,6 +436,21 @@
       The record is formatted, and then sent to the syslog server. If exception
       information is present, it is *not* sent to the server.
 
+      .. versionchanged:: 3.2.1
+         (See: :issue:`12168`.) In earlier versions, the message sent to the
+         syslog daemons was always terminated with a NUL byte, because early
+         versions of these daemons expected a NUL terminated message - even
+         though it's not in the relevant specification (RF 5424). More recent
+         versions of these daemons don't expect the NUL byte but strip it off
+         if it's there, and even more recent daemons (which adhere more closely
+         to RFC 5424) pass the NUL byte on as part of the message.
+
+         To enable easier handling of syslog messages in the face of all these
+         differing daemon behaviours, the appending of the NUL byte has been
+         made configurable, through the use of a class-level attribute,
+         ``append_nul``. This defaults to ``True`` (preserving the existing
+         behaviour) but can be set to ``False`` on a ``SysLogHandler`` instance
+         in order for that instance to *not* append the NUL terminator.
 
    .. method:: encodePriority(facility, priority)
 
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index 32f762d..20cd57c 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -453,6 +453,13 @@
       record. Otherwise, the ISO8601 format is used.  The resulting string is
       returned.
 
+      This function uses a user-configurable function to convert the creation
+      time to a tuple. By default, :func:`time.localtime` is used; to change
+      this for a particular formatter instance, set the ``converter`` attribute
+      to a function with the same signature as :func:`time.localtime` or
+      :func:`time.gmtime`. To change it for all formatters, for example if you
+      want all logging times to be shown in GMT, set the ``converter``
+      attribute in the ``Formatter`` class.
 
    .. method:: formatException(exc_info)
 
@@ -544,6 +551,9 @@
    :param name:  The name of the logger used to log the event represented by
                  this LogRecord.
    :param level: The numeric level of the logging event (one of DEBUG, INFO etc.)
+                 Note that this is converted to *two* attributes of the LogRecord:
+                 ``levelno`` for the numeric value and ``levelname`` for the
+                 corresponding level name.
    :param pathname: The full pathname of the source file where the logging call
                     was made.
    :param lineno: The line number in the source file where the logging call was
diff --git a/Doc/license.rst b/Doc/license.rst
index a32b7ab..b471828 100644
--- a/Doc/license.rst
+++ b/Doc/license.rst
@@ -845,7 +845,7 @@
 -----
 
 The :mod:`pyexpat` extension is built using an included copy of the expat
-sources unless the build is configured :option:`--with-system-expat`::
+sources unless the build is configured ``--with-system-expat``::
 
   Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
                                  and Clark Cooper
@@ -874,7 +874,7 @@
 ------
 
 The :mod:`_ctypes` extension is built using an included copy of the libffi
-sources unless the build is configured :option:`--with-system-libffi`::
+sources unless the build is configured ``--with-system-libffi``::
 
    Copyright (c) 1996-2008  Red Hat, Inc and others.
 
diff --git a/Doc/tutorial/interpreter.rst b/Doc/tutorial/interpreter.rst
index 8d743de..29afc50 100644
--- a/Doc/tutorial/interpreter.rst
+++ b/Doc/tutorial/interpreter.rst
@@ -169,6 +169,8 @@
 suppressed.
 
 
+.. _tut-source-encoding:
+
 Source Code Encoding
 --------------------
 
diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index d1f47eb..16a753c 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -501,7 +501,7 @@
 ~~~~~~~~~~~~~~~~~~~~
 
 Setting these variables only has an effect in a debug build of Python, that is,
-if Python was configured with the :option:`--with-pydebug` build option.
+if Python was configured with the ``--with-pydebug`` build option.
 
 .. envvar:: PYTHONTHREADDEBUG
 
diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def
index 5ddd098..9546e2b 100644
--- a/Lib/idlelib/config-main.def
+++ b/Lib/idlelib/config-main.def
@@ -46,8 +46,8 @@
 [General]
 editor-on-startup= 0
 autosave= 0
-print-command-posix=lpr %s
-print-command-win=start /min notepad /p %s
+print-command-posix=lpr %%s
+print-command-win=start /min notepad /p %%s
 delete-exitfunc= 1
 
 [EditorWindow]
diff --git a/Lib/inspect.py b/Lib/inspect.py
index 4899cbf..bb46ea6 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -518,9 +518,13 @@
     or code object.  The source code is returned as a list of all the lines
     in the file and the line number indexes a line in that list.  An IOError
     is raised if the source code cannot be retrieved."""
-    file = getsourcefile(object)
-    if not file:
+
+    file = getfile(object)
+    sourcefile = getsourcefile(object)
+    if not sourcefile and file[0] + file[-1] != '<>':
         raise IOError('source code not available')
+    file = sourcefile if sourcefile else file
+
     module = getmodule(object, file)
     if module:
         lines = linecache.getlines(file, module.__dict__)
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
index 306cf86..4a6b959 100644
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -766,6 +766,8 @@
         """
         return self.priority_map.get(levelName, "warning")
 
+    append_nul = True   # some old syslog daemons expect a NUL terminator
+
     def emit(self, record):
         """
         Emit a record.
@@ -773,7 +775,9 @@
         The record is formatted, and then sent to the syslog server. If
         exception information is present, it is NOT sent to the server.
         """
-        msg = self.format(record) + '\000'
+        msg = self.format(record)
+        if self.append_nul:
+            msg += '\000'
         """
         We need to convert record level to lowercase, maybe this will
         change in the future.
diff --git a/Lib/netrc.py b/Lib/netrc.py
index a60b8b7..c96db6f 100644
--- a/Lib/netrc.py
+++ b/Lib/netrc.py
@@ -2,7 +2,7 @@
 
 # Module and documentation by Eric S. Raymond, 21 Dec 1998
 
-import os, shlex
+import io, os, shlex
 
 __all__ = ["netrc", "NetrcParseError"]
 
@@ -37,12 +37,14 @@
         lexer.commenters = lexer.commenters.replace('#', '')
         while 1:
             # Look for a machine, default, or macdef top-level keyword
+            saved_lineno = lexer.lineno
             toplevel = tt = lexer.get_token()
             if not tt:
                 break
             elif tt[0] == '#':
-                fp.readline();
-                continue;
+                if lexer.lineno == saved_lineno and len(tt) == 1:
+                    lexer.instream.readline()
+                continue
             elif tt == 'machine':
                 entryname = lexer.get_token()
             elif tt == 'default':
@@ -68,8 +70,8 @@
             self.hosts[entryname] = {}
             while 1:
                 tt = lexer.get_token()
-                if (tt=='' or tt == 'machine' or
-                    tt == 'default' or tt =='macdef'):
+                if (tt.startswith('#') or
+                    tt in {'', 'machine', 'default', 'macdef'}):
                     if password:
                         self.hosts[entryname] = (login, account, password)
                         lexer.push_token(tt)
diff --git a/Lib/ntpath.py b/Lib/ntpath.py
index ec8a7ab..826be87 100644
--- a/Lib/ntpath.py
+++ b/Lib/ntpath.py
@@ -672,3 +672,14 @@
 def sameopenfile(f1, f2):
     """Test whether two file objects reference the same file"""
     return _getfileinformation(f1) == _getfileinformation(f2)
+
+
+try:
+    # The genericpath.isdir implementation uses os.stat and checks the mode
+    # attribute to tell whether or not the path is a directory.
+    # This is overkill on Windows - just pass the path to GetFileAttributes
+    # and check the attribute from there.
+    from nt import _isdir as isdir
+except ImportError:
+    # Use genericpath.isdir as imported above.
+    pass
diff --git a/Lib/smtplib.py b/Lib/smtplib.py
index dbccf48..ce71699 100755
--- a/Lib/smtplib.py
+++ b/Lib/smtplib.py
@@ -162,7 +162,7 @@
         re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
 
 def _quote_periods(bindata):
-    return re.sub(br'(?m)^\.', '..', bindata)
+    return re.sub(br'(?m)^\.', b'..', bindata)
 
 def _fix_eols(data):
     return  re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)
diff --git a/Lib/test/support.py b/Lib/test/support.py
index 9c8f6d3..25aab2e 100644
--- a/Lib/test/support.py
+++ b/Lib/test/support.py
@@ -1487,11 +1487,14 @@
     global _can_symlink
     if _can_symlink is not None:
         return _can_symlink
+    symlink_path = TESTFN + "can_symlink"
     try:
-        os.symlink(TESTFN, TESTFN + "can_symlink")
+        os.symlink(TESTFN, symlink_path)
         can = True
     except (OSError, NotImplementedError, AttributeError):
         can = False
+    else:
+        os.remove(symlink_path)
     _can_symlink = can
     return can
 
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
index f5dff1e..7666fe4 100644
--- a/Lib/test/test_inspect.py
+++ b/Lib/test/test_inspect.py
@@ -298,6 +298,23 @@
         del sys.modules[name]
         inspect.getmodule(compile('a=10','','single'))
 
+    def test_proceed_with_fake_filename(self):
+        '''doctest monkeypatches linecache to enable inspection'''
+        fn, source = '<test>', 'def x(): pass\n'
+        getlines = linecache.getlines
+        def monkey(filename, module_globals=None):
+            if filename == fn:
+                return source.splitlines(True)
+            else:
+                return getlines(filename, module_globals)
+        linecache.getlines = monkey
+        try:
+            ns = {}
+            exec(compile(source, fn, 'single'), ns)
+            inspect.getsource(ns["x"])
+        finally:
+            linecache.getlines = getlines
+
 class TestDecorators(GetSourceBase):
     fodderModule = mod2
 
diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py
index da7ec05..ef70e37 100644
--- a/Lib/test/test_netrc.py
+++ b/Lib/test/test_netrc.py
@@ -1,54 +1,107 @@
-
-import netrc, os, unittest, sys
+import netrc, os, unittest, sys, textwrap
 from test import support
 
-TEST_NETRC = """
-
- #this is a comment
-#this is a comment
-# this is a comment
-
-machine foo login log1 password pass1 account acct1
-machine bar login log1 password pass# account acct1
-
-macdef macro1
-line1
-line2
-
-macdef macro2
-line3
-line4
-
-default login log2 password pass2
-
-"""
-
 temp_filename = support.TESTFN
 
 class NetrcTestCase(unittest.TestCase):
 
-    def setUp(self):
-        mode = 'w'
-        if sys.platform not in ['cygwin']:
-            mode += 't'
-        fp = open(temp_filename, mode)
-        fp.write(TEST_NETRC)
-        fp.close()
-        self.nrc = netrc.netrc(temp_filename)
-
     def tearDown(self):
         os.unlink(temp_filename)
 
-    def test_case_1(self):
-        self.assertEqual(self.nrc.hosts['foo'], ('log1', 'acct1', 'pass1'))
-        self.assertEqual(self.nrc.hosts['default'], ('log2', None, 'pass2'))
+    def make_nrc(self, test_data):
+        test_data = textwrap.dedent(test_data)
+        mode = 'w'
+        if sys.platform != 'cygwin':
+            mode += 't'
+        with open(temp_filename, mode) as fp:
+            fp.write(test_data)
+        return netrc.netrc(temp_filename)
+
+    def test_default(self):
+        nrc = self.make_nrc("""\
+            machine host1.domain.com login log1 password pass1 account acct1
+            default login log2 password pass2
+            """)
+        self.assertEqual(nrc.hosts['host1.domain.com'],
+                         ('log1', 'acct1', 'pass1'))
+        self.assertEqual(nrc.hosts['default'], ('log2', None, 'pass2'))
 
     def test_macros(self):
-        self.assertEqual(self.nrc.macros, {'macro1':['line1\n', 'line2\n'],
-                                           'macro2':['line3\n', 'line4\n']})
+        nrc = self.make_nrc("""\
+            macdef macro1
+            line1
+            line2
 
-    def test_parses_passwords_with_hash_character(self):
-        self.assertEqual(self.nrc.hosts['bar'], ('log1', 'acct1', 'pass#'))
+            macdef macro2
+            line3
+            line4
+            """)
+        self.assertEqual(nrc.macros, {'macro1': ['line1\n', 'line2\n'],
+                                      'macro2': ['line3\n', 'line4\n']})
+
+    def _test_passwords(self, nrc, passwd):
+        nrc = self.make_nrc(nrc)
+        self.assertEqual(nrc.hosts['host.domain.com'], ('log', 'acct', passwd))
+
+    def test_password_with_leading_hash(self):
+        self._test_passwords("""\
+            machine host.domain.com login log password #pass account acct
+            """, '#pass')
+
+    def test_password_with_trailing_hash(self):
+        self._test_passwords("""\
+            machine host.domain.com login log password pass# account acct
+            """, 'pass#')
+
+    def test_password_with_internal_hash(self):
+        self._test_passwords("""\
+            machine host.domain.com login log password pa#ss account acct
+            """, 'pa#ss')
+
+    def _test_comment(self, nrc, passwd='pass'):
+        nrc = self.make_nrc(nrc)
+        self.assertEqual(nrc.hosts['foo.domain.com'], ('bar', None, passwd))
+        self.assertEqual(nrc.hosts['bar.domain.com'], ('foo', None, 'pass'))
+
+    def test_comment_before_machine_line(self):
+        self._test_comment("""\
+            # comment
+            machine foo.domain.com login bar password pass
+            machine bar.domain.com login foo password pass
+            """)
+
+    def test_comment_before_machine_line_no_space(self):
+        self._test_comment("""\
+            #comment
+            machine foo.domain.com login bar password pass
+            machine bar.domain.com login foo password pass
+            """)
+
+    def test_comment_before_machine_line_hash_only(self):
+        self._test_comment("""\
+            #
+            machine foo.domain.com login bar password pass
+            machine bar.domain.com login foo password pass
+            """)
+
+    def test_comment_at_end_of_machine_line(self):
+        self._test_comment("""\
+            machine foo.domain.com login bar password pass # comment
+            machine bar.domain.com login foo password pass
+            """)
+
+    def test_comment_at_end_of_machine_line_no_space(self):
+        self._test_comment("""\
+            machine foo.domain.com login bar password pass #comment
+            machine bar.domain.com login foo password pass
+            """)
+
+    def test_comment_at_end_of_machine_line_pass_has_hash(self):
+        self._test_comment("""\
+            machine foo.domain.com login bar password #pass #comment
+            machine bar.domain.com login foo password pass
+            """, '#pass')
+
 
 def test_main():
     support.run_unittest(NetrcTestCase)
diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py
index 884529f..dd92044 100644
--- a/Lib/test/test_smtplib.py
+++ b/Lib/test/test_smtplib.py
@@ -278,6 +278,21 @@
         mexpect = '%s%s\n%s' % (MSG_BEGIN, m.decode('ascii'), MSG_END)
         self.assertEqual(self.output.getvalue(), mexpect)
 
+    def testSendNeedingDotQuote(self):
+        # Issue 12283
+        m = '.A test\n.mes.sage.'
+        smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
+        smtp.sendmail('John', 'Sally', m)
+        # XXX (see comment in testSend)
+        time.sleep(0.01)
+        smtp.quit()
+
+        self.client_evt.set()
+        self.serv_evt.wait()
+        self.output.flush()
+        mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END)
+        self.assertEqual(self.output.getvalue(), mexpect)
+
     def testSendMessage(self):
         m = email.mime.text.MIMEText('A test message')
         smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py
index 90aab86..782020c 100644
--- a/Lib/test/test_zipfile.py
+++ b/Lib/test/test_zipfile.py
@@ -351,6 +351,24 @@
             with zipfile.ZipFile(f, "r") as zipfp:
                 self.assertEqual(zipfp.namelist(), [TESTFN])
 
+    def test_ignores_newline_at_end(self):
+        with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
+            zipfp.write(TESTFN, TESTFN)
+        with open(TESTFN2, 'a') as f:
+            f.write("\r\n\00\00\00")
+        with zipfile.ZipFile(TESTFN2, "r") as zipfp:
+            self.assertIsInstance(zipfp, zipfile.ZipFile)
+
+    def test_ignores_stuff_appended_past_comments(self):
+        with zipfile.ZipFile(TESTFN2, "w", zipfile.ZIP_STORED) as zipfp:
+            zipfp.comment = b"this is a comment"
+            zipfp.write(TESTFN, TESTFN)
+        with open(TESTFN2, 'a') as f:
+            f.write("abcdef\r\n")
+        with zipfile.ZipFile(TESTFN2, "r") as zipfp:
+            self.assertIsInstance(zipfp, zipfile.ZipFile)
+            self.assertEqual(zipfp.comment, b"this is a comment")
+
     def test_write_default_name(self):
         """Check that calling ZipFile.write without arcname specified
         produces the expected result."""
diff --git a/Lib/zipfile.py b/Lib/zipfile.py
index 50f4848..5cc7816 100644
--- a/Lib/zipfile.py
+++ b/Lib/zipfile.py
@@ -246,16 +246,14 @@
         # found the magic number; attempt to unpack and interpret
         recData = data[start:start+sizeEndCentDir]
         endrec = list(struct.unpack(structEndArchive, recData))
-        comment = data[start+sizeEndCentDir:]
-        # check that comment length is correct
-        if endrec[_ECD_COMMENT_SIZE] == len(comment):
-            # Append the archive comment and start offset
-            endrec.append(comment)
-            endrec.append(maxCommentStart + start)
+        commentSize = endrec[_ECD_COMMENT_SIZE] #as claimed by the zip file
+        comment = data[start+sizeEndCentDir:start+sizeEndCentDir+commentSize]
+        endrec.append(comment)
+        endrec.append(maxCommentStart + start)
 
-            # Try to read the "Zip64 end of central directory" structure
-            return _EndRecData64(fpin, maxCommentStart + start - filesize,
-                                 endrec)
+        # Try to read the "Zip64 end of central directory" structure
+        return _EndRecData64(fpin, maxCommentStart + start - filesize,
+                             endrec)
 
     # Unable to find a valid end of central directory structure
     return
diff --git a/Misc/ACKS b/Misc/ACKS
index aa077fc..d578c48 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -609,6 +609,7 @@
 James A Morrison
 Pablo Mouzo
 Mher Movsisyan
+Ruslan Mstoi
 Sjoerd Mullender
 Sape Mullender
 Michael Muller
diff --git a/Misc/NEWS b/Misc/NEWS
index 06c08a9..75d1082 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -25,6 +25,21 @@
 Library
 -------
 
+- Issue #9284: Allow inspect.findsource() to find the source of doctest
+  functions.
+
+- Issue #12009: Fixed regression in netrc file comment handling.
+
+- Issue #10694: zipfile now ignores garbage at the end of a zipfile.
+
+- Issue #12283: Fixed regression in smtplib quoting of leading dots in DATA.
+
+- Issue #12168: SysLogHandler now allows NUL termination to be controlled using
+  a new 'append_nul' attribute on the handler.
+
+- Issue #11583: Speed up os.path.isdir on Windows by using GetFileAttributes
+  instead of os.stat.
+
 - Named tuples now work correctly with vars().
 
 - Issue #12085: Fix an attribute error in subprocess.Popen destructor if the
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 674eeb2..b6f49b8 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -2866,6 +2866,45 @@
                                 info.nFileIndexHigh,
                                 info.nFileIndexLow);
 }
+
+PyDoc_STRVAR(posix__isdir__doc__,
+"Return true if the pathname refers to an existing directory.");
+
+static PyObject *
+posix__isdir(PyObject *self, PyObject *args)
+{
+    PyObject *opath;
+    char *path;
+    PyUnicodeObject *po;
+    DWORD attributes;
+
+    if (PyArg_ParseTuple(args, "U|:_isdir", &po)) {
+        Py_UNICODE *wpath = PyUnicode_AS_UNICODE(po);
+
+        attributes = GetFileAttributesW(wpath);
+        if (attributes == INVALID_FILE_ATTRIBUTES)
+            Py_RETURN_FALSE;
+        goto check;
+    }
+    /* Drop the argument parsing error as narrow strings
+       are also valid. */
+    PyErr_Clear();
+
+    if (!PyArg_ParseTuple(args, "O&:_isdir",
+                          PyUnicode_FSConverter, &opath))
+        return NULL;
+
+    path = PyBytes_AsString(opath);
+    attributes = GetFileAttributesA(path);
+    if (attributes == INVALID_FILE_ATTRIBUTES)
+        Py_RETURN_FALSE;
+
+check:
+    if (attributes & FILE_ATTRIBUTE_DIRECTORY)
+        Py_RETURN_TRUE;
+    else
+        Py_RETURN_FALSE;
+}
 #endif /* MS_WINDOWS */
 
 PyDoc_STRVAR(posix_mkdir__doc__,
@@ -8102,6 +8141,7 @@
     {"_getfullpathname",        posix__getfullpathname, METH_VARARGS, NULL},
     {"_getfinalpathname",       posix__getfinalpathname, METH_VARARGS, NULL},
     {"_getfileinformation",     posix__getfileinformation, METH_VARARGS, NULL},
+    {"_isdir",                  posix__isdir, METH_VARARGS, posix__isdir__doc__},
 #endif
 #ifdef HAVE_GETLOADAVG
     {"getloadavg",      posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__},