summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Curtin <brian@python.org>2011-06-13 21:10:32 (GMT)
committerBrian Curtin <brian@python.org>2011-06-13 21:10:32 (GMT)
commita87d586fd6c4a5dfe4ea89fda288ae8eb66b0134 (patch)
tree420fe483c679d2225cc8bd82eefaecf783999268
parentd25aef55c8b0025dd2ee7de11b526f34ceed6b66 (diff)
parentcdc751720ee7a13ac163da1eedf3a513386c98d4 (diff)
downloadcpython-a87d586fd6c4a5dfe4ea89fda288ae8eb66b0134.zip
cpython-a87d586fd6c4a5dfe4ea89fda288ae8eb66b0134.tar.gz
cpython-a87d586fd6c4a5dfe4ea89fda288ae8eb66b0134.tar.bz2
branch merge?
-rw-r--r--.hgtags1
-rw-r--r--Doc/c-api/intro.rst4
-rw-r--r--Doc/distutils/introduction.rst10
-rw-r--r--Doc/documenting/markup.rst11
-rw-r--r--Doc/install/index.rst15
-rw-r--r--Doc/library/builtins.rst4
-rw-r--r--Doc/library/constants.rst2
-rw-r--r--Doc/library/logging.handlers.rst15
-rw-r--r--Doc/library/logging.rst10
-rw-r--r--Doc/license.rst4
-rw-r--r--Doc/tutorial/interpreter.rst2
-rw-r--r--Doc/using/cmdline.rst2
-rw-r--r--Lib/idlelib/config-main.def4
-rw-r--r--Lib/inspect.py8
-rw-r--r--Lib/logging/handlers.py6
-rw-r--r--Lib/netrc.py12
-rw-r--r--Lib/ntpath.py11
-rwxr-xr-xLib/smtplib.py2
-rw-r--r--Lib/test/support.py5
-rw-r--r--Lib/test/test_inspect.py17
-rw-r--r--Lib/test/test_netrc.py131
-rw-r--r--Lib/test/test_smtplib.py15
-rw-r--r--Lib/test/test_zipfile.py18
-rw-r--r--Lib/zipfile.py18
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS15
-rw-r--r--Modules/posixmodule.c40
27 files changed, 303 insertions, 80 deletions
diff --git a/.hgtags b/.hgtags
index 407bbcb..c12bf22 100644
--- a/.hgtags
+++ b/.hgtags
@@ -76,6 +76,7 @@ a69a031ac1402dede8b1ef80096436bca6d371f3 v3.1
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 @@ frequently-used builds will be described in the remainder of this section.
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 @@ Some observations:
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 @@ following example shows all of the features of this directive type::
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 @@ in the name of the downloaded archive, e.g. :file:`foo-1.0.tar.gz` or
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 @@ Standard Build and Install
==========================
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 @@ supports sending logging messages to a remote or local Unix syslog.
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 @@ The useful mapping keys in a :class:`LogRecord` are given in the section on
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 @@ wire).
: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 @@ expat
-----
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 @@ libffi
------
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 @@ also be ``.pyw``, in that case, the console window that normally appears is
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 @@ Debug-mode variables
~~~~~~~~~~~~~~~~~~~~
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 @@ def findsource(object):
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 @@ class SysLogHandler(logging.Handler):
"""
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 @@ class SysLogHandler(logging.Handler):
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 @@ class netrc:
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 @@ class netrc:
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 @@ except ImportError:
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 @@ def quotedata(data):
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 @@ def can_symlink():
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 @@ class TestRetrievingSourceCode(GetSourceBase):
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
+
+ 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_parses_passwords_with_hash_character(self):
- self.assertEqual(self.nrc.hosts['bar'], ('log1', 'acct1', '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 @@ class DebuggingServerTests(unittest.TestCase):
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 @@ class TestsWithSourceFile(unittest.TestCase):
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 @@ def _EndRecData(fpin):
# 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)
-
- # Try to read the "Zip64 end of central directory" structure
- return _EndRecData64(fpin, maxCommentStart + start - filesize,
- endrec)
+ 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)
# 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 @@ Derek Morr
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 @@ Core and Builtins
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 @@ posix__getfileinformation(PyObject *self, PyObject *args)
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 @@ static PyMethodDef posix_methods[] = {
{"_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__},