diff options
-rw-r--r-- | Doc/library/curses.rst | 46 | ||||
-rw-r--r-- | Doc/library/faulthandler.rst | 2 | ||||
-rw-r--r-- | Lib/curses/__init__.py | 46 | ||||
-rw-r--r-- | Lib/curses/wrapper.py | 50 | ||||
-rw-r--r-- | Lib/email/header.py | 10 | ||||
-rw-r--r-- | Lib/mailbox.py | 14 | ||||
-rw-r--r-- | Lib/test/test_email/test_email.py | 17 | ||||
-rw-r--r-- | Lib/test/test_mailbox.py | 13 | ||||
-rw-r--r-- | Misc/ACKS | 1 | ||||
-rw-r--r-- | Misc/NEWS | 11 | ||||
-rw-r--r-- | Tools/msi/msi.py | 4 |
11 files changed, 118 insertions, 96 deletions
diff --git a/Doc/library/curses.rst b/Doc/library/curses.rst index 1ca22c8..73b957e 100644 --- a/Doc/library/curses.rst +++ b/Doc/library/curses.rst @@ -41,10 +41,6 @@ Linux and the BSD variants of Unix. 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 @@ The module :mod:`curses` defines the following functions: 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 @@ You can instantiate a :class:`Textbox` object as follows: 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@thyrsus.com> -.. sectionauthor:: Eric Raymond <esr@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 index b0f8cb4..3e75c60 100644 --- a/Doc/library/faulthandler.rst +++ b/Doc/library/faulthandler.rst @@ -24,7 +24,7 @@ tracebacks: * 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 index bd7d5f6..5cd4eda 100644 --- a/Lib/curses/__init__.py +++ b/Lib/curses/__init__.py @@ -13,7 +13,6 @@ the package, and perhaps a particular module inside it. __revision__ = "$Id$" from _curses import * -from curses.wrapper import wrapper import os as _os import sys as _sys @@ -57,3 +56,48 @@ try: 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 index 5183ce7..0000000 --- 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 index 0a66df5..2e687b7 100644 --- a/Lib/email/header.py +++ b/Lib/email/header.py @@ -73,9 +73,10 @@ def decode_header(header): 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 @@ class Header: 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 index b96b270..e23ea8c 100644 --- a/Lib/mailbox.py +++ b/Lib/mailbox.py @@ -1923,9 +1923,10 @@ class _ProxyFile: 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 @@ class _ProxyFile: @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 @@ class _PartialFile(_ProxyFile): 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 index 1657afc..17451f3 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -4324,12 +4324,27 @@ A very long line that must get split to something other than at the 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 index f5abb41..18aeec7 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -297,6 +297,13 @@ class TestMailbox(TestBase): 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 @@ class TestProxyFileBase(TestBase): def _test_close(self, proxy): # Close a file + self.assertFalse(proxy.closed) + proxy.close() + self.assertTrue(proxy.closed) + # Issue 11700 subsequent closes should be a no-op. proxy.close() - self.assertRaises(AttributeError, lambda: proxy.close()) + self.assertTrue(proxy.closed) class TestProxyFile(TestProxyFileBase): @@ -1013,6 +1013,7 @@ David Wolever Klaus-Juergen Wolf Dan Wolfe Richard Wolff +Adam Woodbeck Darren Worrall Gordon Worley Thomas Wouters @@ -193,6 +193,17 @@ Core and Builtins 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 index 18638eb..de1a623 100644 --- a/Tools/msi/msi.py +++ b/Tools/msi/msi.py @@ -1057,14 +1057,14 @@ def add_files(db): 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)) |