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

benjamin.peterson python-checkins at python.org
Sun Jun 19 16:34:35 CEST 2011


http://hg.python.org/cpython/rev/a7e00934baf1
changeset:   70871:a7e00934baf1
parent:      70870:29517ee4919d
parent:      70869:0eec89955a2f
user:        Benjamin Peterson <benjamin at python.org>
date:        Sun Jun 19 09:38:02 2011 -0500
summary:
  merge heads

files:
  Doc/library/curses.rst            |  46 ++++------------
  Doc/library/faulthandler.rst      |   2 +-
  Lib/curses/__init__.py            |  46 +++++++++++++++++-
  Lib/curses/wrapper.py             |  50 -------------------
  Lib/email/header.py               |  10 ++-
  Lib/mailbox.py                    |  14 +++-
  Lib/test/test_email/test_email.py |  17 ++++++-
  Lib/test/test_mailbox.py          |  13 ++++-
  Misc/ACKS                         |   1 +
  Misc/NEWS                         |  11 ++++
  Tools/msi/msi.py                  |   4 +-
  11 files changed, 118 insertions(+), 96 deletions(-)


diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst
--- a/Doc/library/curses.rst
+++ b/Doc/library/curses.rst
@@ -41,10 +41,6 @@
    Module :mod:`curses.textpad`
       Editable text widget for curses supporting  :program:`Emacs`\ -like bindings.
 
-   Module :mod:`curses.wrapper`
-      Convenience function to ensure proper terminal setup and resetting on
-      application entry and exit.
-
    :ref:`curses-howto`
       Tutorial material on using curses with Python, by Andrew Kuchling and Eric
       Raymond.
@@ -592,6 +588,19 @@
    foreground color on the default background.
 
 
+.. function:: wrapper(func, ...)
+
+   Initialize curses and call another callable object, *func*, which should be the
+   rest of your curses-using application.  If the application raises an exception,
+   this function will restore the terminal to a sane state before re-raising the
+   exception and generating a traceback.  The callable object *func* is then passed
+   the main window 'stdscr' as its first argument, followed by any other arguments
+   passed to :func:`wrapper`.  Before calling *func*, :func:`wrapper` turns on
+   cbreak mode, turns off echo, enables the terminal keypad, and initializes colors
+   if the terminal has color support.  On exit (whether normally or by exception)
+   it restores cooked mode, turns on echo, and disables the terminal keypad.
+
+
 .. _curses-window-objects:
 
 Window Objects
@@ -1659,32 +1668,3 @@
       cursor motion that would land the cursor on a trailing blank goes to the
       end of that line instead, and trailing blanks are stripped when the window
       contents are gathered.
-
-
-:mod:`curses.wrapper` --- Terminal handler for curses programs
-==============================================================
-
-.. module:: curses.wrapper
-   :synopsis: Terminal configuration wrapper for curses programs.
-.. moduleauthor:: Eric Raymond <esr at thyrsus.com>
-.. sectionauthor:: Eric Raymond <esr at thyrsus.com>
-
-
-This module supplies one function, :func:`wrapper`, which runs another function
-which should be the rest of your curses-using application.  If the application
-raises an exception, :func:`wrapper` will restore the terminal to a sane state
-before re-raising the exception and generating a traceback.
-
-
-.. function:: wrapper(func, ...)
-
-   Wrapper function that initializes curses and calls another function, *func*,
-   restoring normal keyboard/screen behavior on error. The callable object *func*
-   is then passed the main window 'stdscr' as its first argument, followed by any
-   other arguments passed to :func:`wrapper`.
-
-Before calling the hook function, :func:`wrapper` turns on cbreak mode, turns
-off echo, enables the terminal keypad, and initializes colors if the terminal
-has color support.  On exit (whether normally or by exception) it restores
-cooked mode, turns on echo, and disables the terminal keypad.
-
diff --git a/Doc/library/faulthandler.rst b/Doc/library/faulthandler.rst
--- a/Doc/library/faulthandler.rst
+++ b/Doc/library/faulthandler.rst
@@ -24,7 +24,7 @@
 * Only ASCII is supported. The ``backslashreplace`` error handler is used on
   encoding.
 * Each string is limited to 100 characters.
-* Only the the filename, the function name and the line number are
+* Only the filename, the function name and the line number are
   displayed. (no source code)
 * It is limited to 100 frames and 100 threads.
 
diff --git a/Lib/curses/__init__.py b/Lib/curses/__init__.py
--- a/Lib/curses/__init__.py
+++ b/Lib/curses/__init__.py
@@ -13,7 +13,6 @@
 __revision__ = "$Id$"
 
 from _curses import *
-from curses.wrapper import wrapper
 import os as _os
 import sys as _sys
 
@@ -57,3 +56,48 @@
     has_key
 except NameError:
     from has_key import has_key
+
+# Wrapper for the entire curses-based application.  Runs a function which
+# should be the rest of your curses-based application.  If the application
+# raises an exception, wrapper() will restore the terminal to a sane state so
+# you can read the resulting traceback.
+
+def wrapper(func, *args, **kwds):
+    """Wrapper function that initializes curses and calls another function,
+    restoring normal keyboard/screen behavior on error.
+    The callable object 'func' is then passed the main window 'stdscr'
+    as its first argument, followed by any other arguments passed to
+    wrapper().
+    """
+
+    try:
+        # Initialize curses
+        stdscr = initscr()
+
+        # Turn off echoing of keys, and enter cbreak mode,
+        # where no buffering is performed on keyboard input
+        noecho()
+        cbreak()
+
+        # In keypad mode, escape sequences for special keys
+        # (like the cursor keys) will be interpreted and
+        # a special value like curses.KEY_LEFT will be returned
+        stdscr.keypad(1)
+
+        # Start color, too.  Harmless if the terminal doesn't have
+        # color; user can test with has_color() later on.  The try/catch
+        # works around a minor bit of over-conscientiousness in the curses
+        # module -- the error return from C start_color() is ignorable.
+        try:
+            start_color()
+        except:
+            pass
+
+        return func(stdscr, *args, **kwds)
+    finally:
+        # Set everything back to normal
+        if 'stdscr' in locals():
+            stdscr.keypad(0)
+            echo()
+            nocbreak()
+            endwin()
diff --git a/Lib/curses/wrapper.py b/Lib/curses/wrapper.py
deleted file mode 100644
--- a/Lib/curses/wrapper.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""curses.wrapper
-
-Contains one function, wrapper(), which runs another function which
-should be the rest of your curses-based application.  If the
-application raises an exception, wrapper() will restore the terminal
-to a sane state so you can read the resulting traceback.
-
-"""
-
-import curses
-
-def wrapper(func, *args, **kwds):
-    """Wrapper function that initializes curses and calls another function,
-    restoring normal keyboard/screen behavior on error.
-    The callable object 'func' is then passed the main window 'stdscr'
-    as its first argument, followed by any other arguments passed to
-    wrapper().
-    """
-
-    try:
-        # Initialize curses
-        stdscr = curses.initscr()
-
-        # Turn off echoing of keys, and enter cbreak mode,
-        # where no buffering is performed on keyboard input
-        curses.noecho()
-        curses.cbreak()
-
-        # In keypad mode, escape sequences for special keys
-        # (like the cursor keys) will be interpreted and
-        # a special value like curses.KEY_LEFT will be returned
-        stdscr.keypad(1)
-
-        # Start color, too.  Harmless if the terminal doesn't have
-        # color; user can test with has_color() later on.  The try/catch
-        # works around a minor bit of over-conscientiousness in the curses
-        # module -- the error return from C start_color() is ignorable.
-        try:
-            curses.start_color()
-        except:
-            pass
-
-        return func(stdscr, *args, **kwds)
-    finally:
-        # Set everything back to normal
-        if 'stdscr' in locals():
-            stdscr.keypad(0)
-            curses.echo()
-            curses.nocbreak()
-            curses.endwin()
diff --git a/Lib/email/header.py b/Lib/email/header.py
--- a/Lib/email/header.py
+++ b/Lib/email/header.py
@@ -73,9 +73,10 @@
     An email.errors.HeaderParseError may be raised when certain decoding error
     occurs (e.g. a base64 decoding exception).
     """
-    # If it is a Header object, we can just return the chunks.
+    # If it is a Header object, we can just return the encoded chunks.
     if hasattr(header, '_chunks'):
-        return list(header._chunks)
+        return [(_charset._encode(string, str(charset)), str(charset))
+                    for string, charset in header._chunks]
     # If no encoding, just return the header with no charset.
     if not ecre.search(header):
         return [(header, None)]
@@ -274,7 +275,10 @@
             charset = Charset(charset)
         if not isinstance(s, str):
             input_charset = charset.input_codec or 'us-ascii'
-            s = s.decode(input_charset, errors)
+            if input_charset == _charset.UNKNOWN8BIT:
+                s = s.decode('us-ascii', 'surrogateescape')
+            else:
+                s = s.decode(input_charset, errors)
         # Ensure that the bytes we're storing can be decoded to the output
         # character set, otherwise an early error is thrown.
         output_charset = charset.output_codec or 'us-ascii'
diff --git a/Lib/mailbox.py b/Lib/mailbox.py
--- a/Lib/mailbox.py
+++ b/Lib/mailbox.py
@@ -1923,9 +1923,10 @@
 
     def close(self):
         """Close the file."""
-        if hasattr(self._file, 'close'):
-            self._file.close()
-        del self._file
+        if hasattr(self, '_file'):
+            if hasattr(self._file, 'close'):
+                self._file.close()
+            del self._file
 
     def _read(self, size, read_method):
         """Read size bytes using read_method."""
@@ -1957,6 +1958,10 @@
 
     @property
     def closed(self):
+        if not hasattr(self, '_file'):
+            return True
+        if not hasattr(self._file, 'closed'):
+            return False
         return self._file.closed
 
 
@@ -1995,7 +2000,8 @@
     def close(self):
         # do *not* close the underlying file object for partial files,
         # since it's global to the mailbox object
-        del self._file
+        if hasattr(self, '_file'):
+            del self._file
 
 
 def _lock_file(f, dotlock=True):
diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py
--- a/Lib/test/test_email/test_email.py
+++ b/Lib/test/test_email/test_email.py
@@ -4324,12 +4324,27 @@
 
     def test_escaped_8bit_header(self):
         x = b'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big'
-        x = x.decode('ascii', 'surrogateescape')
+        e = x.decode('ascii', 'surrogateescape')
+        h = Header(e, charset=email.charset.UNKNOWN8BIT)
+        self.assertEqual(str(h),
+                        'Ynwp4dUEbay Auction Semiar- No Charge \uFFFD Earn Big')
+        self.assertEqual(email.header.decode_header(h), [(x, 'unknown-8bit')])
+
+    def test_header_handles_binary_unknown8bit(self):
+        x = b'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big'
         h = Header(x, charset=email.charset.UNKNOWN8BIT)
         self.assertEqual(str(h),
                         'Ynwp4dUEbay Auction Semiar- No Charge \uFFFD Earn Big')
         self.assertEqual(email.header.decode_header(h), [(x, 'unknown-8bit')])
 
+    def test_make_header_handles_binary_unknown8bit(self):
+        x = b'Ynwp4dUEbay Auction Semiar- No Charge \x96 Earn Big'
+        h = Header(x, charset=email.charset.UNKNOWN8BIT)
+        h2 = email.header.make_header(email.header.decode_header(h))
+        self.assertEqual(str(h2),
+                        'Ynwp4dUEbay Auction Semiar- No Charge \uFFFD Earn Big')
+        self.assertEqual(email.header.decode_header(h2), [(x, 'unknown-8bit')])
+
     def test_modify_returned_list_does_not_change_header(self):
         h = Header('test')
         chunks = email.header.decode_header(h)
diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py
--- a/Lib/test/test_mailbox.py
+++ b/Lib/test/test_mailbox.py
@@ -297,6 +297,13 @@
         self.assertEqual(data1.decode('ascii').replace(os.linesep, '\n'),
                          _sample_message)
 
+    def test_get_file_can_be_closed_twice(self):
+        # Issue 11700
+        key = self._box.add(_sample_message)
+        f = self._box.get_file(key)
+        f.close()
+        f.close()
+
     def test_iterkeys(self):
         # Get keys using iterkeys()
         self._check_iteration(self._box.keys, do_keys=True, do_values=False)
@@ -1862,8 +1869,12 @@
 
     def _test_close(self, proxy):
         # Close a file
+        self.assertFalse(proxy.closed)
         proxy.close()
-        self.assertRaises(AttributeError, lambda: proxy.close())
+        self.assertTrue(proxy.closed)
+        # Issue 11700 subsequent closes should be a no-op.
+        proxy.close()
+        self.assertTrue(proxy.closed)
 
 
 class TestProxyFile(TestProxyFileBase):
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1013,6 +1013,7 @@
 Klaus-Juergen Wolf
 Dan Wolfe
 Richard Wolff
+Adam Woodbeck
 Darren Worrall
 Gordon Worley
 Thomas Wouters
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -193,6 +193,17 @@
 Library
 -------
 
+- Issue #6771: moved the curses.wrapper function from the single-function
+  wrapper module into __init__, eliminating the module.  Since __init__ was
+  already importing the function to curses.wrapper, there is no API change.
+
+- Issue #11584: email.header.decode_header no longer fails if the header
+  passed to it is a Header object, and Header/make_header no longer fail
+  if given binary unknown-8bit input.
+
+- Issue #11700: mailbox proxy object close methods can now be called multiple
+  times without error.
+
 - Issue #11767: Correct file descriptor leak in mailbox's __getitem__ method.
 
 - Issue #12133: AbstractHTTPHandler.do_open() of urllib.request closes the HTTP
diff --git a/Tools/msi/msi.py b/Tools/msi/msi.py
--- a/Tools/msi/msi.py
+++ b/Tools/msi/msi.py
@@ -1057,14 +1057,14 @@
             lib.add_file("turtle.cfg")
         if dir=="pydoc_data":
             lib.add_file("_pydoc.css")
-        if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email":
+        if dir=="data" and parent.physical=="test_email":
             # This should contain all non-.svn files listed in subversion
             for f in os.listdir(lib.absolute):
                 if f.endswith(".txt") or f==".svn":continue
                 if f.endswith(".au") or f.endswith(".gif"):
                     lib.add_file(f)
                 else:
-                    print("WARNING: New file %s in email/test/data" % f)
+                    print("WARNING: New file %s in test/test_email/data" % f)
         for f in os.listdir(lib.absolute):
             if os.path.isdir(os.path.join(lib.absolute, f)):
                 pydirs.append((lib, f))

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


More information about the Python-checkins mailing list