Merged revisions 58221-58741 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r58221 | georg.brandl | 2007-09-20 10:57:59 -0700 (Thu, 20 Sep 2007) | 2 lines

  Patch #1181: add os.environ.clear() method.
........
  r58225 | sean.reifschneider | 2007-09-20 23:33:28 -0700 (Thu, 20 Sep 2007) | 3 lines

  Issue1704287: "make install" fails unless you do "make" first.  Make
     oldsharedmods and sharedmods in "libinstall".
........
  r58232 | guido.van.rossum | 2007-09-22 13:18:03 -0700 (Sat, 22 Sep 2007) | 4 lines

  Patch # 188 by Philip Jenvey.
  Make tell() mark CRLF as a newline.
  With unit test.
........
  r58242 | georg.brandl | 2007-09-24 10:55:47 -0700 (Mon, 24 Sep 2007) | 2 lines

  Fix typo and double word.
........
  r58245 | georg.brandl | 2007-09-24 10:59:28 -0700 (Mon, 24 Sep 2007) | 2 lines

  #1196: document default radix for int().
........
  r58247 | georg.brandl | 2007-09-24 11:08:24 -0700 (Mon, 24 Sep 2007) | 2 lines

  #1177: accept 2xx responses for https too, not only http.
........
  r58249 | andrew.kuchling | 2007-09-24 16:45:51 -0700 (Mon, 24 Sep 2007) | 1 line

  Remove stray odd character; grammar fix
........
  r58250 | andrew.kuchling | 2007-09-24 16:46:28 -0700 (Mon, 24 Sep 2007) | 1 line

  Typo fix
........
  r58251 | andrew.kuchling | 2007-09-24 17:09:42 -0700 (Mon, 24 Sep 2007) | 1 line

  Add various items
........
  r58268 | vinay.sajip | 2007-09-26 22:34:45 -0700 (Wed, 26 Sep 2007) | 1 line

  Change to flush and close logic to fix #1760556.
........
  r58269 | vinay.sajip | 2007-09-26 22:38:51 -0700 (Wed, 26 Sep 2007) | 1 line

  Change to basicConfig() to fix #1021.
........
  r58270 | georg.brandl | 2007-09-26 23:26:58 -0700 (Wed, 26 Sep 2007) | 2 lines

  #1208: document match object's boolean value.
........
  r58271 | vinay.sajip | 2007-09-26 23:56:13 -0700 (Wed, 26 Sep 2007) | 1 line

  Minor date change.
........
  r58272 | vinay.sajip | 2007-09-27 00:35:10 -0700 (Thu, 27 Sep 2007) | 1 line

  Change to LogRecord.__init__() to fix #1206. Note that archaic use of type(x) == types.DictType is because of keeping 1.5.2 compatibility. While this is much less relevant these days, there probably needs to be a separate commit for removing all archaic constructs at the same time.
........
  r58288 | brett.cannon | 2007-09-30 12:45:10 -0700 (Sun, 30 Sep 2007) | 9 lines

  tuple.__repr__ did not consider a reference loop as it is not possible from
  Python code; but it is possible from C.  object.__str__ had the issue of not
  expecting a type to doing something within it's tp_str implementation that
  could trigger an infinite recursion, but it could in C code..  Both found
  thanks to BaseException and how it handles its repr.

  Closes issue #1686386.  Thanks to Thomas Herve for taking an initial stab at
  coming up with a solution.
........
  r58289 | brett.cannon | 2007-09-30 13:37:19 -0700 (Sun, 30 Sep 2007) | 3 lines

  Fix error introduced by r58288; if a tuple is length 0 return its repr and
  don't worry about any self-referring tuples.
........
  r58294 | facundo.batista | 2007-10-02 10:01:24 -0700 (Tue, 02 Oct 2007) | 11 lines


  Made the various is_* operations return booleans.  This was discussed
  with Cawlishaw by mail, and he basically confirmed that to these is_*
  operations, there's no need to return Decimal(0) and Decimal(1) if
  the language supports the False and True booleans.

  Also added a few tests for the these functions in extra.decTest, since
  they are mostly untested (apart from the doctests).

  Thanks Mark Dickinson
........
  r58295 | facundo.batista | 2007-10-02 11:21:18 -0700 (Tue, 02 Oct 2007) | 4 lines


  Added a class to store the digits of log(10), so that they can be made
  available when necessary without recomputing.  Thanks Mark Dickinson
........
  r58299 | mark.summerfield | 2007-10-03 01:53:21 -0700 (Wed, 03 Oct 2007) | 4 lines

  Added note in footnote about string comparisons about
  unicodedata.normalize().
........
  r58304 | raymond.hettinger | 2007-10-03 14:18:11 -0700 (Wed, 03 Oct 2007) | 1 line

  enumerate() is no longer bounded to using sequences shorter than LONG_MAX.  The possibility of overflow was sending some newsgroup posters into a tizzy.
........
  r58305 | raymond.hettinger | 2007-10-03 17:20:27 -0700 (Wed, 03 Oct 2007) | 1 line

  itertools.count() no longer limited to sys.maxint.
........
  r58306 | kurt.kaiser | 2007-10-03 18:49:54 -0700 (Wed, 03 Oct 2007) | 3 lines

  Assume that the user knows when he wants to end the line; don't insert
  something he didn't select or complete.
........
  r58307 | kurt.kaiser | 2007-10-03 19:07:50 -0700 (Wed, 03 Oct 2007) | 2 lines

  Remove unused theme that was causing a fault in p3k.
........
  r58308 | kurt.kaiser | 2007-10-03 19:09:17 -0700 (Wed, 03 Oct 2007) | 2 lines

  Clean up EditorWindow close.
........
  r58309 | kurt.kaiser | 2007-10-03 19:53:07 -0700 (Wed, 03 Oct 2007) | 7 lines

  textView cleanup. Patch 1718043 Tal Einat.

  M    idlelib/EditorWindow.py
  M    idlelib/aboutDialog.py
  M    idlelib/textView.py
  M    idlelib/NEWS.txt
........
  r58310 | kurt.kaiser | 2007-10-03 20:11:12 -0700 (Wed, 03 Oct 2007) | 3 lines

  configDialog cleanup. Patch 1730217 Tal Einat.
........
  r58311 | neal.norwitz | 2007-10-03 23:00:48 -0700 (Wed, 03 Oct 2007) | 4 lines

  Coverity #151: Remove deadcode.

  All this code already exists above starting at line 653.
........
  r58325 | fred.drake | 2007-10-04 19:46:12 -0700 (Thu, 04 Oct 2007) | 1 line

  wrap lines to <80 characters before fixing errors
........
  r58326 | raymond.hettinger | 2007-10-04 19:47:07 -0700 (Thu, 04 Oct 2007) | 6 lines

  Add __asdict__() to NamedTuple and refine the docs.
  Add maxlen support to deque() and fixup docs.
  Partially fix __reduce__().  The None as a third arg was no longer supported.
  Still needs work on __reduce__() to handle recursive inputs.
........
  r58327 | fred.drake | 2007-10-04 19:48:32 -0700 (Thu, 04 Oct 2007) | 3 lines

  move descriptions of ac_(in|out)_buffer_size to the right place
  http://bugs.python.org/issue1053
........
  r58329 | neal.norwitz | 2007-10-04 20:39:17 -0700 (Thu, 04 Oct 2007) | 3 lines

  dict could be NULL, so we need to XDECREF.
  Fix a compiler warning about passing a PyTypeObject* instead of PyObject*.
........
  r58330 | neal.norwitz | 2007-10-04 20:41:19 -0700 (Thu, 04 Oct 2007) | 2 lines

  Fix Coverity #158: Check the correct variable.
........
  r58332 | neal.norwitz | 2007-10-04 22:01:38 -0700 (Thu, 04 Oct 2007) | 7 lines

  Fix Coverity #159.

  This code was broken if save() returned a negative number since i contained
  a boolean value and then we compared i < 0 which should never be true.

  Will backport (assuming it's necessary)
........
  r58334 | neal.norwitz | 2007-10-04 22:29:17 -0700 (Thu, 04 Oct 2007) | 1 line

  Add a note about fixing some more warnings found by Coverity.
........
  r58338 | raymond.hettinger | 2007-10-05 12:07:31 -0700 (Fri, 05 Oct 2007) | 1 line

  Restore BEGIN/END THREADS macros which were squashed in the previous checkin
........
  r58343 | gregory.p.smith | 2007-10-06 00:48:10 -0700 (Sat, 06 Oct 2007) | 3 lines

  Stab in the dark attempt to fix the test_bsddb3 failure on sparc and S-390
  ubuntu buildbots.
........
  r58344 | gregory.p.smith | 2007-10-06 00:51:59 -0700 (Sat, 06 Oct 2007) | 2 lines

  Allows BerkeleyDB 4.6.x >= 4.6.21 for the bsddb module.
........
  r58348 | gregory.p.smith | 2007-10-06 08:47:37 -0700 (Sat, 06 Oct 2007) | 3 lines

  Use the host the author likely meant in the first place.  pop.gmail.com is
  reliable.  gmail.org is someones personal domain.
........
  r58351 | neal.norwitz | 2007-10-06 12:16:28 -0700 (Sat, 06 Oct 2007) | 3 lines

  Ensure that this test will pass even if another test left an unwritable TESTFN.
  Also use the safe unlink in test_support instead of rolling our own here.
........
  r58368 | georg.brandl | 2007-10-08 00:50:24 -0700 (Mon, 08 Oct 2007) | 3 lines

  #1123: fix the docs for the str.split(None, sep) case.
  Also expand a few other methods' docs, which had more info in the deprecated string module docs.
........
  r58369 | georg.brandl | 2007-10-08 01:06:05 -0700 (Mon, 08 Oct 2007) | 2 lines

  Update docstring of sched, also remove an unused assignment.
........
  r58370 | raymond.hettinger | 2007-10-08 02:14:28 -0700 (Mon, 08 Oct 2007) | 5 lines

  Add comments to NamedTuple code.
  Let the field spec be either a string or a non-string sequence (suggested by Martin Blais with use cases).
  Improve the error message in the case of a SyntaxError (caused by a duplicate field name).
........
  r58371 | raymond.hettinger | 2007-10-08 02:56:29 -0700 (Mon, 08 Oct 2007) | 1 line

  Missed a line in the docs
........
  r58372 | raymond.hettinger | 2007-10-08 03:11:51 -0700 (Mon, 08 Oct 2007) | 1 line

  Better variable names
........
  r58376 | georg.brandl | 2007-10-08 07:12:47 -0700 (Mon, 08 Oct 2007) | 3 lines

  #1199: docs for tp_as_{number,sequence,mapping}, by Amaury Forgeot d'Arc.
  No need to merge this to py3k!
........
  r58380 | raymond.hettinger | 2007-10-08 14:26:58 -0700 (Mon, 08 Oct 2007) | 1 line

  Eliminate camelcase function name
........
  r58381 | andrew.kuchling | 2007-10-08 16:23:03 -0700 (Mon, 08 Oct 2007) | 1 line

  Eliminate camelcase function name
........
  r58382 | raymond.hettinger | 2007-10-08 18:36:23 -0700 (Mon, 08 Oct 2007) | 1 line

  Make the error messages more specific
........
  r58384 | gregory.p.smith | 2007-10-08 23:02:21 -0700 (Mon, 08 Oct 2007) | 10 lines

  Splits Modules/_bsddb.c up into bsddb.h and _bsddb.c and adds a C API
  object available as bsddb.db.api.  This is based on the patch submitted
  by Duncan Grisby here:
    http://sourceforge.net/tracker/index.php?func=detail&aid=1551895&group_id=13900&atid=313900
  See this thread for additional info:
    http://sourceforge.net/mailarchive/forum.php?thread_name=E1GAVDK-0002rk-Iw%40apasphere.com&forum_name=pybsddb-users

  It also cleans up the code a little by removing some ifdef/endifs for
  python prior to 2.1 and for unsupported Berkeley DB <= 3.2.
........
  r58385 | gregory.p.smith | 2007-10-08 23:50:43 -0700 (Mon, 08 Oct 2007) | 5 lines

  Fix a double free when positioning a database cursor to a non-existant
  string key (and probably a few other situations with string keys).
  This was reported with a patch as pybsddb sourceforge bug 1708868 by
  jjjhhhlll at gmail.
........
  r58386 | gregory.p.smith | 2007-10-09 00:19:11 -0700 (Tue, 09 Oct 2007) | 3 lines

  Use the highest cPickle protocol in bsddb.dbshelve.  This comes from
  sourceforge pybsddb patch 1551443 by w_barnes.
........
  r58394 | gregory.p.smith | 2007-10-09 11:26:02 -0700 (Tue, 09 Oct 2007) | 2 lines

  remove another sleepycat reference
........
  r58396 | kurt.kaiser | 2007-10-09 12:31:30 -0700 (Tue, 09 Oct 2007) | 3 lines

  Allow interrupt only when executing user code in subprocess
  Patch 1225 Tal Einat modified from IDLE-Spoon.
........
  r58399 | brett.cannon | 2007-10-09 17:07:50 -0700 (Tue, 09 Oct 2007) | 5 lines

  Remove file-level typedefs that were inconsistently used throughout the file.
  Just move over to the public API names.

  Closes issue1238.
........
  r58401 | raymond.hettinger | 2007-10-09 17:26:46 -0700 (Tue, 09 Oct 2007) | 1 line

  Accept Jim Jewett's api suggestion to use None instead of -1 to indicate unbounded deques.
........
  r58403 | kurt.kaiser | 2007-10-09 17:55:40 -0700 (Tue, 09 Oct 2007) | 2 lines

  Allow cursor color change w/o restart. Patch 1725576 Tal Einat.
........
  r58404 | kurt.kaiser | 2007-10-09 18:06:47 -0700 (Tue, 09 Oct 2007) | 2 lines

  show paste if > 80 columns.  Patch 1659326 Tal Einat.
........
  r58415 | thomas.heller | 2007-10-11 12:51:32 -0700 (Thu, 11 Oct 2007) | 5 lines

  On OS X, use os.uname() instead of gestalt.sysv(...) to get the
  operating system version.  This allows to use ctypes when Python
  was configured with --disable-toolbox-glue.
........
  r58419 | neal.norwitz | 2007-10-11 20:01:01 -0700 (Thu, 11 Oct 2007) | 1 line

  Get rid of warning about not being able to create an existing directory.
........
  r58420 | neal.norwitz | 2007-10-11 20:01:30 -0700 (Thu, 11 Oct 2007) | 1 line

  Get rid of warnings on a bunch of platforms by using a proper prototype.
........
  r58421 | neal.norwitz | 2007-10-11 20:01:54 -0700 (Thu, 11 Oct 2007) | 4 lines

  Get rid of compiler warning about retval being used (returned) without
  being initialized.  (gcc warning and Coverity 202)
........
  r58422 | neal.norwitz | 2007-10-11 20:03:23 -0700 (Thu, 11 Oct 2007) | 1 line

  Fix Coverity 168:  Close the file before returning (exiting).
........
  r58423 | neal.norwitz | 2007-10-11 20:04:18 -0700 (Thu, 11 Oct 2007) | 4 lines

  Fix Coverity 180:  Don't overallocate.  We don't need structs, but pointers.
  Also fix a memory leak.
........
  r58424 | neal.norwitz | 2007-10-11 20:05:19 -0700 (Thu, 11 Oct 2007) | 5 lines

  Fix Coverity 185-186:  If the passed in FILE is NULL, uninitialized memory
  would be accessed.

  Will backport.
........
  r58425 | neal.norwitz | 2007-10-11 20:52:34 -0700 (Thu, 11 Oct 2007) | 1 line

  Get this module to compile with bsddb versions prior to 4.3
........
  r58430 | martin.v.loewis | 2007-10-12 01:56:52 -0700 (Fri, 12 Oct 2007) | 3 lines

  Bug #1216: Restore support for Visual Studio 2002.
  Will backport to 2.5.
........
  r58433 | raymond.hettinger | 2007-10-12 10:53:11 -0700 (Fri, 12 Oct 2007) | 1 line

  Fix test of count.__repr__() to ignore the 'L' if the count is a long
........
  r58434 | gregory.p.smith | 2007-10-12 11:44:06 -0700 (Fri, 12 Oct 2007) | 4 lines

  Fixes http://bugs.python.org/issue1233 - bsddb.dbshelve.DBShelf.append
  was useless due to inverted logic.  Also adds a test case for RECNO dbs
  to test_dbshelve.
........
  r58445 | georg.brandl | 2007-10-13 06:20:03 -0700 (Sat, 13 Oct 2007) | 2 lines

  Fix email example.
........
  r58450 | gregory.p.smith | 2007-10-13 16:02:05 -0700 (Sat, 13 Oct 2007) | 2 lines

  Fix an uncollectable reference leak in bsddb.db.DBShelf.append
........
  r58453 | neal.norwitz | 2007-10-13 17:18:40 -0700 (Sat, 13 Oct 2007) | 8 lines

  Let the O/S supply a port if none of the default ports can be used.
  This should make the tests more robust at the expense of allowing
  tests to be sloppier by not requiring them to cleanup after themselves.
  (It will legitamitely help when running two test suites simultaneously
  or if another process is already using one of the predefined ports.)

  Also simplifies (slightLy) the exception handling elsewhere.
........
  r58459 | neal.norwitz | 2007-10-14 11:30:21 -0700 (Sun, 14 Oct 2007) | 2 lines

  Don't raise a string exception, they don't work anymore.
........
  r58460 | neal.norwitz | 2007-10-14 11:40:37 -0700 (Sun, 14 Oct 2007) | 1 line

  Use unittest for assertions
........
  r58468 | armin.rigo | 2007-10-15 00:48:35 -0700 (Mon, 15 Oct 2007) | 2 lines

  test_bigbits was not testing what it seemed to.
........
  r58471 | guido.van.rossum | 2007-10-15 08:54:11 -0700 (Mon, 15 Oct 2007) | 3 lines

  Change a PyErr_Print() into a PyErr_Clear(),
  per discussion in issue 1031213.
........
  r58500 | raymond.hettinger | 2007-10-16 12:18:30 -0700 (Tue, 16 Oct 2007) | 1 line

  Improve error messages
........
  r58506 | raymond.hettinger | 2007-10-16 14:28:32 -0700 (Tue, 16 Oct 2007) | 1 line

  More docs, error messages, and tests
........
  r58507 | andrew.kuchling | 2007-10-16 15:58:03 -0700 (Tue, 16 Oct 2007) | 1 line

  Add items
........
  r58508 | brett.cannon | 2007-10-16 16:24:06 -0700 (Tue, 16 Oct 2007) | 3 lines

  Remove ``:const:`` notation on None in parameter list.  Since the markup is not
  rendered for parameters it just showed up as ``:const:`None` `` in the output.
........
  r58509 | brett.cannon | 2007-10-16 16:26:45 -0700 (Tue, 16 Oct 2007) | 3 lines

  Re-order some functions whose parameters differ between PyObject and const char
  * so that they are next to each other.
........
  r58522 | armin.rigo | 2007-10-17 11:46:37 -0700 (Wed, 17 Oct 2007) | 5 lines

  Fix the overflow checking of list_repeat.
  Introduce overflow checking into list_inplace_repeat.

  Backport candidate, possibly.
........
  r58530 | facundo.batista | 2007-10-17 20:16:03 -0700 (Wed, 17 Oct 2007) | 7 lines


  Issue #1580738.  When HTTPConnection reads the whole stream with read(),
  it closes itself.  When the stream is read in several calls to read(n),
  it should behave in the same way if HTTPConnection knows where the end
  of the stream is (through self.length).  Added a test case for this
  behaviour.
........
  r58531 | facundo.batista | 2007-10-17 20:44:48 -0700 (Wed, 17 Oct 2007) | 3 lines


  Issue 1289, just a typo.
........
  r58532 | gregory.p.smith | 2007-10-18 00:56:54 -0700 (Thu, 18 Oct 2007) | 4 lines

  cleanup test_dbtables to use mkdtemp.  cleanup dbtables to pass txn as a
  keyword argument whenever possible to avoid bugs and confusion.  (dbtables.py
  line 447 self.db.get using txn as a non-keyword was an actual bug due to this)
........
  r58533 | gregory.p.smith | 2007-10-18 01:34:20 -0700 (Thu, 18 Oct 2007) | 4 lines

  Fix a weird bug in dbtables: if it chose a random rowid string that contained
  NULL bytes it would cause the database all sorts of problems in the future
  leading to very strange random failures and corrupt dbtables.bsdTableDb dbs.
........
  r58534 | gregory.p.smith | 2007-10-18 09:32:02 -0700 (Thu, 18 Oct 2007) | 3 lines

  A cleaner fix than the one committed last night.  Generate random rowids that
  do not contain null bytes.
........
  r58537 | gregory.p.smith | 2007-10-18 10:17:57 -0700 (Thu, 18 Oct 2007) | 2 lines

  mention bsddb fixes.
........
  r58538 | raymond.hettinger | 2007-10-18 14:13:06 -0700 (Thu, 18 Oct 2007) | 1 line

  Remove useless warning
........
  r58539 | gregory.p.smith | 2007-10-19 00:31:20 -0700 (Fri, 19 Oct 2007) | 2 lines

  squelch the warning that this test is supposed to trigger.
........
  r58542 | georg.brandl | 2007-10-19 05:32:39 -0700 (Fri, 19 Oct 2007) | 2 lines

  Clarify wording for apply().
........
  r58544 | mark.summerfield | 2007-10-19 05:48:17 -0700 (Fri, 19 Oct 2007) | 3 lines

  Added a cross-ref to each other.
........
  r58545 | georg.brandl | 2007-10-19 10:38:49 -0700 (Fri, 19 Oct 2007) | 2 lines

  #1284: "S" means "seen", not unread.
........
  r58548 | thomas.heller | 2007-10-19 11:11:41 -0700 (Fri, 19 Oct 2007) | 4 lines

  Fix ctypes on 32-bit systems when Python is configured --with-system-ffi.
  See also https://bugs.launchpad.net/bugs/72505.

  Ported from release25-maint branch.
........
  r58550 | facundo.batista | 2007-10-19 12:25:57 -0700 (Fri, 19 Oct 2007) | 8 lines


  The constructor from tuple was way too permissive: it allowed bad
  coefficient numbers, floats in the sign, and other details that
  generated directly the wrong number in the best case, or triggered
  misfunctionality in the alorithms.

  Test cases added for these issues. Thanks Mark Dickinson.
........
  r58559 | georg.brandl | 2007-10-20 06:22:53 -0700 (Sat, 20 Oct 2007) | 2 lines

  Fix code being interpreted as a target.
........
  r58561 | georg.brandl | 2007-10-20 06:36:24 -0700 (Sat, 20 Oct 2007) | 2 lines

  Document new "cmdoption" directive.
........
  r58562 | georg.brandl | 2007-10-20 08:21:22 -0700 (Sat, 20 Oct 2007) | 2 lines

  Make a path more Unix-standardy.
........
  r58564 | georg.brandl | 2007-10-20 10:51:39 -0700 (Sat, 20 Oct 2007) | 2 lines

  Document new directive "envvar".
........
  r58567 | georg.brandl | 2007-10-20 11:08:14 -0700 (Sat, 20 Oct 2007) | 6 lines

  * Add new toplevel chapter, "Using Python." (how to install,
    configure and setup python on different platforms -- at least
    in theory.)
  * Move the Python on Mac docs in that chapter.
  * Add a new chapter about the command line invocation, by stargaming.
........
  r58568 | georg.brandl | 2007-10-20 11:33:20 -0700 (Sat, 20 Oct 2007) | 2 lines

  Change title, for now.
........
  r58569 | georg.brandl | 2007-10-20 11:39:25 -0700 (Sat, 20 Oct 2007) | 2 lines

  Add entry to ACKS.
........
  r58570 | georg.brandl | 2007-10-20 12:05:45 -0700 (Sat, 20 Oct 2007) | 2 lines

  Clarify -E docs.
........
  r58571 | georg.brandl | 2007-10-20 12:08:36 -0700 (Sat, 20 Oct 2007) | 2 lines

  Even more clarification.
........
  r58572 | andrew.kuchling | 2007-10-20 12:25:37 -0700 (Sat, 20 Oct 2007) | 1 line

  Fix protocol name
........
  r58573 | andrew.kuchling | 2007-10-20 12:35:18 -0700 (Sat, 20 Oct 2007) | 1 line

  Various items
........
  r58574 | andrew.kuchling | 2007-10-20 12:39:35 -0700 (Sat, 20 Oct 2007) | 1 line

  Use correct header line
........
  r58576 | armin.rigo | 2007-10-21 02:14:15 -0700 (Sun, 21 Oct 2007) | 3 lines

  Add a crasher for the long-standing issue with closing a file
  while another thread uses it.
........
  r58577 | georg.brandl | 2007-10-21 03:01:56 -0700 (Sun, 21 Oct 2007) | 2 lines

  Remove duplicate crasher.
........
  r58578 | georg.brandl | 2007-10-21 03:24:20 -0700 (Sun, 21 Oct 2007) | 2 lines

  Unify "byte code" to "bytecode". Also sprinkle :term: markup for it.
........
  r58579 | georg.brandl | 2007-10-21 03:32:54 -0700 (Sun, 21 Oct 2007) | 2 lines

  Add markup to new function descriptions.
........
  r58580 | georg.brandl | 2007-10-21 03:45:46 -0700 (Sun, 21 Oct 2007) | 2 lines

  Add :term:s for descriptors.
........
  r58581 | georg.brandl | 2007-10-21 03:46:24 -0700 (Sun, 21 Oct 2007) | 2 lines

  Unify "file-descriptor" to "file descriptor".
........
  r58582 | georg.brandl | 2007-10-21 03:52:38 -0700 (Sun, 21 Oct 2007) | 2 lines

  Add :term: for generators.
........
  r58583 | georg.brandl | 2007-10-21 05:10:28 -0700 (Sun, 21 Oct 2007) | 2 lines

  Add :term:s for iterator.
........
  r58584 | georg.brandl | 2007-10-21 05:15:05 -0700 (Sun, 21 Oct 2007) | 2 lines

  Add :term:s for "new-style class".
........
  r58588 | neal.norwitz | 2007-10-21 21:47:54 -0700 (Sun, 21 Oct 2007) | 1 line

  Add Chris Monson so he can edit PEPs.
........
  r58594 | guido.van.rossum | 2007-10-22 09:27:19 -0700 (Mon, 22 Oct 2007) | 4 lines

  Issue #1307, patch by Derek Shockey.
  When "MAIL" is received without args, an exception happens instead of
  sending a 501 syntax error response.
........
  r58598 | travis.oliphant | 2007-10-22 19:40:56 -0700 (Mon, 22 Oct 2007) | 1 line

  Add phuang patch from Issue 708374 which adds offset parameter to mmap module.
........
  r58601 | neal.norwitz | 2007-10-22 22:44:27 -0700 (Mon, 22 Oct 2007) | 2 lines

  Bug #1313, fix typo (wrong variable name) in example.
........
  r58609 | georg.brandl | 2007-10-23 11:21:35 -0700 (Tue, 23 Oct 2007) | 2 lines

  Update Pygments version from externals.
........
  r58618 | guido.van.rossum | 2007-10-23 12:25:41 -0700 (Tue, 23 Oct 2007) | 3 lines

  Issue 1307 by Derek Shockey, fox the same bug for RCPT.
  Neal: please backport!
........
  r58620 | raymond.hettinger | 2007-10-23 13:37:41 -0700 (Tue, 23 Oct 2007) | 1 line

  Shorter name for namedtuple()
........
  r58621 | andrew.kuchling | 2007-10-23 13:55:47 -0700 (Tue, 23 Oct 2007) | 1 line

  Update name
........
  r58622 | raymond.hettinger | 2007-10-23 14:23:07 -0700 (Tue, 23 Oct 2007) | 1 line

  Fixup news entry
........
  r58623 | raymond.hettinger | 2007-10-23 18:28:33 -0700 (Tue, 23 Oct 2007) | 1 line

  Optimize sum() for integer and float inputs.
........
  r58624 | raymond.hettinger | 2007-10-23 19:05:51 -0700 (Tue, 23 Oct 2007) | 1 line

  Fixup error return and add support for intermixed ints and floats/
........
  r58628 | vinay.sajip | 2007-10-24 03:47:06 -0700 (Wed, 24 Oct 2007) | 1 line

  Bug #1321: Fixed logic error in TimedRotatingFileHandler.__init__()
........
  r58641 | facundo.batista | 2007-10-24 12:11:08 -0700 (Wed, 24 Oct 2007) | 4 lines


  Issue 1290.  CharacterData.__repr__ was constructing a string
  in response that keeped having a non-ascii character.
........
  r58643 | thomas.heller | 2007-10-24 12:50:45 -0700 (Wed, 24 Oct 2007) | 1 line

  Added unittest for calling a function with paramflags (backport from py3k branch).
........
  r58645 | matthias.klose | 2007-10-24 13:00:44 -0700 (Wed, 24 Oct 2007) | 2 lines

  - Build using system ffi library on arm*-linux*.
........
  r58651 | georg.brandl | 2007-10-24 14:40:38 -0700 (Wed, 24 Oct 2007) | 2 lines

  Bug #1287: make os.environ.pop() work as expected.
........
  r58652 | raymond.hettinger | 2007-10-24 19:26:58 -0700 (Wed, 24 Oct 2007) | 1 line

  Missing DECREFs
........
  r58653 | matthias.klose | 2007-10-24 23:37:24 -0700 (Wed, 24 Oct 2007) | 2 lines

  - Build using system ffi library on arm*-linux*, pass --with-system-ffi to CONFIG_ARGS
........
  r58655 | thomas.heller | 2007-10-25 12:47:32 -0700 (Thu, 25 Oct 2007) | 2 lines

  ffi_type_longdouble may be already #defined.
  See issue 1324.
........
  r58656 | kurt.kaiser | 2007-10-25 15:43:45 -0700 (Thu, 25 Oct 2007) | 3 lines

  Correct an ancient bug in an unused path by removing that path: register() is
  now idempotent.
........
  r58660 | kurt.kaiser | 2007-10-25 17:10:09 -0700 (Thu, 25 Oct 2007) | 4 lines

  1. Add comments to provide top-level documentation.
  2. Refactor to use more descriptive names.
  3. Enhance tests in main().
........
  r58675 | georg.brandl | 2007-10-26 11:30:41 -0700 (Fri, 26 Oct 2007) | 2 lines

  Fix new pop() method on os.environ on ignorecase-platforms.
........
  r58696 | neal.norwitz | 2007-10-27 15:32:21 -0700 (Sat, 27 Oct 2007) | 1 line

  Update URL for Pygments.  0.8.1 is no longer available
........
  r58697 | hyeshik.chang | 2007-10-28 04:19:02 -0700 (Sun, 28 Oct 2007) | 3 lines

  - Add support for FreeBSD 8 which is recently forked from FreeBSD 7.
  - Regenerate IN module for most recent maintenance tree of FreeBSD 6 and 7.
........
  r58698 | hyeshik.chang | 2007-10-28 05:38:09 -0700 (Sun, 28 Oct 2007) | 2 lines

  Enable platform-specific tweaks for FreeBSD 8 (exactly same to FreeBSD 7's yet)
........
  r58700 | kurt.kaiser | 2007-10-28 12:03:59 -0700 (Sun, 28 Oct 2007) | 2 lines

  Add confirmation dialog before printing.  Patch 1717170 Tal Einat.
........
  r58706 | guido.van.rossum | 2007-10-29 13:52:45 -0700 (Mon, 29 Oct 2007) | 3 lines

  Patch 1353 by Jacob Winther.
  Add mp4 mapping to mimetypes.py.
........
  r58709 | guido.van.rossum | 2007-10-29 15:15:05 -0700 (Mon, 29 Oct 2007) | 6 lines

  Backport fixes for the code that decodes octal escapes (and for PyString
  also hex escapes) -- this was reaching beyond the end of the input string
  buffer, even though it is not supposed to be \0-terminated.
  This has no visible effect but is clearly the correct thing to do.
  (In 3.0 it had a visible effect after removing ob_sstate from PyString.)
........
  r58710 | kurt.kaiser | 2007-10-29 19:38:54 -0700 (Mon, 29 Oct 2007) | 7 lines

  check in Tal Einat's update to tabpage.py
  Patch 1612746

  M    configDialog.py
  M    NEWS.txt
  AM   tabbedpages.py
........
  r58715 | georg.brandl | 2007-10-30 10:51:18 -0700 (Tue, 30 Oct 2007) | 2 lines

  Use correct markup.
........
  r58716 | georg.brandl | 2007-10-30 10:57:12 -0700 (Tue, 30 Oct 2007) | 2 lines

  Make example about hiding None return values at the prompt clearer.
........
  r58728 | neal.norwitz | 2007-10-30 23:33:20 -0700 (Tue, 30 Oct 2007) | 1 line

  Fix some compiler warnings for signed comparisons on Unix and Windows.
........
  r58731 | martin.v.loewis | 2007-10-31 10:19:33 -0700 (Wed, 31 Oct 2007) | 2 lines

  Adding Christian Heimes.
........
  r58737 | raymond.hettinger | 2007-10-31 14:57:58 -0700 (Wed, 31 Oct 2007) | 1 line

  Clarify the reasons why pickle is almost always better than marshal
........
  r58739 | raymond.hettinger | 2007-10-31 15:15:49 -0700 (Wed, 31 Oct 2007) | 1 line

  Sets are marshalable.
........
diff --git a/Lib/idlelib/AutoComplete.py b/Lib/idlelib/AutoComplete.py
index b187741..b2b1657 100644
--- a/Lib/idlelib/AutoComplete.py
+++ b/Lib/idlelib/AutoComplete.py
@@ -27,7 +27,7 @@
 
     menudefs = [
         ('edit', [
-            ("Show completions", "<<force-open-completions>>"),
+            ("Show Completions", "<<force-open-completions>>"),
         ])
     ]
 
diff --git a/Lib/idlelib/AutoCompleteWindow.py b/Lib/idlelib/AutoCompleteWindow.py
index 785979e..ca9109b 100644
--- a/Lib/idlelib/AutoCompleteWindow.py
+++ b/Lib/idlelib/AutoCompleteWindow.py
@@ -283,20 +283,9 @@
             self._selection_changed()
             return "break"
 
-        elif keysym == "Return" and not state:
-            # If start is a prefix of the selection, or there was an indication
-            # that the user used the completion window, put the selected
-            # completion in the text, and close the list.
-            # Otherwise, close the window and let the event through.
-            cursel = int(self.listbox.curselection()[0])
-            if self.completions[cursel][:len(self.start)] == self.start or \
-               self.userwantswindow:
-                self._change_start(self.completions[cursel])
-                self.hide_window()
-                return "break"
-            else:
-                self.hide_window()
-                return
+        elif keysym == "Return":
+            self.hide_window()
+            return
 
         elif (self.mode == COMPLETE_ATTRIBUTES and keysym in
               ("period", "space", "parenleft", "parenright", "bracketleft",
diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py
index a0a7b47..0cd668a 100644
--- a/Lib/idlelib/EditorWindow.py
+++ b/Lib/idlelib/EditorWindow.py
@@ -386,7 +386,7 @@
 
     def help_dialog(self, event=None):
         fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
-        textView.TextViewer(self.top,'Help',fn)
+        textView.view_file(self.top,'Help',fn)
 
     def python_docs(self, event=None):
         if sys.platform[:3] == 'win':
@@ -408,6 +408,7 @@
 
     def paste(self,event):
         self.text.event_generate("<<Paste>>")
+        self.text.see("insert")
         return "break"
 
     def select_all(self, event=None):
@@ -549,7 +550,8 @@
 
     def close_hook(self):
         if self.flist:
-            self.flist.close_edit(self)
+            self.flist.unregister_maybe_terminate(self)
+            self.flist = None
 
     def set_close_hook(self, close_hook):
         self.close_hook = close_hook
@@ -828,22 +830,21 @@
         if self.io.filename:
             self.update_recent_files_list(new_file=self.io.filename)
         WindowList.unregister_callback(self.postwindowsmenu)
-        if self.close_hook:
-            self.close_hook()
-        self.flist = None
-        colorizing = 0
         self.unload_extensions()
-        self.io.close(); self.io = None
-        self.undo = None # XXX
+        self.io.close()
+        self.io = None
+        self.undo = None
         if self.color:
-            colorizing = self.color.colorizing
-            doh = colorizing and self.top
-            self.color.close(doh) # Cancel colorization
+            self.color.close(False)
+            self.color = None
         self.text = None
         self.tkinter_vars = None
-        self.per.close(); self.per = None
-        if not colorizing:
-            self.top.destroy()
+        self.per.close()
+        self.per = None
+        self.top.destroy()
+        if self.close_hook:
+            # unless override: unregister from flist, terminate if last window
+            self.close_hook()
 
     def load_extensions(self):
         self.extensions = {}
@@ -1501,6 +1502,7 @@
         filename = None
     edit = EditorWindow(root=root, filename=filename)
     edit.set_close_hook(root.quit)
+    edit.text.bind("<<close-all-windows>>", edit.close_event)
     root.mainloop()
     root.destroy()
 
diff --git a/Lib/idlelib/FileList.py b/Lib/idlelib/FileList.py
index 2f5053f..53e30b4 100644
--- a/Lib/idlelib/FileList.py
+++ b/Lib/idlelib/FileList.py
@@ -55,7 +55,7 @@
                 break
         return "break"
 
-    def close_edit(self, edit):
+    def unregister_maybe_terminate(self, edit):
         try:
             key = self.inversedict[edit]
         except KeyError:
diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py
index b970dba..b4de84e 100644
--- a/Lib/idlelib/IOBinding.py
+++ b/Lib/idlelib/IOBinding.py
@@ -485,13 +485,23 @@
             self.text.insert("end-1c", "\n")
 
     def print_window(self, event):
+        m = tkMessageBox.Message(
+            title="Print",
+            message="Print to Default Printer",
+            icon=tkMessageBox.QUESTION,
+            type=tkMessageBox.OKCANCEL,
+            default=tkMessageBox.OK,
+            master=self.text)
+        reply = m.show()
+        if reply != tkMessageBox.OK:
+            self.text.focus_set()
+            return "break"
         tempfilename = None
         saved = self.get_saved()
         if saved:
             filename = self.filename
         # shell undo is reset after every prompt, looks saved, probably isn't
         if not saved or filename is None:
-            # XXX KBK 08Jun03 Wouldn't it be better to ask the user to save?
             (tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
             filename = tempfilename
             os.close(tfd)
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 460b5b5..0966152 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -30,6 +30,24 @@
 
 *Release date: XX-XXX-200X*  UNRELEASED, but merged into 3.0a1
 
+- tabpage.py updated: tabbedPages.py now supports multiple dynamic rows
+  of tabs.  Patch 1612746 Tal Einat.
+
+- Add confirmation dialog before printing.  Patch 1717170 Tal Einat.
+
+- Show paste position if > 80 col.  Patch 1659326 Tal Einat.
+
+- Update cursor color without restarting.  Patch 1725576 Tal Einat.
+
+- Allow keyboard interrupt only when user code is executing in subprocess.
+  Patch 1225 Tal Einat (reworked from IDLE-Spoon).
+
+- configDialog cleanup. Patch 1730217 Tal Einat.
+
+- textView cleanup. Patch 1718043 Tal Einat.
+
+- Clean up EditorWindow close.
+
 - Corrected some bugs in AutoComplete.  Also, Page Up/Down in ACW implemented;
   mouse and cursor selection in ACWindow implemented; double Tab inserts
   current selection and closes ACW (similar to double-click and Return); scroll
@@ -51,6 +69,8 @@
 - Bug #813342: Start the IDLE subprocess with -Qnew if the parent
   is started with that option.
 
+- Honor the "Cancel" action in the save dialog (Debian bug #299092)
+
 - Some syntax errors were being caught by tokenize during the tabnanny
   check, resulting in obscure error messages.  Do the syntax check
   first.  Bug 1562716, 1562719
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 3f9f567..097818f 100644
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -296,9 +296,6 @@
             "stdout": idleConf.GetHighlight(theme, "stdout"),
             "stderr": idleConf.GetHighlight(theme, "stderr"),
             "console": idleConf.GetHighlight(theme, "console"),
-            ### KBK 10Aug07: None tag doesn't seem to serve a purpose and
-            ### breaks in py3k.  Comment out for now.
-            #None: idleConf.GetHighlight(theme, "normal"),
         })
 
 class ModifiedUndoDelegator(UndoDelegator):
diff --git a/Lib/idlelib/WidgetRedirector.py b/Lib/idlelib/WidgetRedirector.py
index 459fea7..3df5a9a 100644
--- a/Lib/idlelib/WidgetRedirector.py
+++ b/Lib/idlelib/WidgetRedirector.py
@@ -1,17 +1,38 @@
 from Tkinter import *
 
-
 class WidgetRedirector:
 
-    """Support for redirecting arbitrary widget subcommands."""
+    """Support for redirecting arbitrary widget subcommands.
 
+    Some Tk operations don't normally pass through Tkinter.  For example, if a
+    character is inserted into a Text widget by pressing a key, a default Tk
+    binding to the widget's 'insert' operation is activated, and the Tk library
+    processes the insert without calling back into Tkinter.
+
+    Although a binding to <Key> could be made via Tkinter, what we really want
+    to do is to hook the Tk 'insert' operation itself.
+
+    When a widget is instantiated, a Tcl command is created whose name is the
+    same as the pathname widget._w.  This command is used to invoke the various
+    widget operations, e.g. insert (for a Text widget). We are going to hook
+    this command and provide a facility ('register') to intercept the widget
+    operation.
+
+    In IDLE, the function being registered provides access to the top of a
+    Percolator chain.  At the bottom of the chain is a call to the original
+    Tk widget operation.
+
+    """
     def __init__(self, widget):
-        self.dict = {}
-        self.widget = widget
-        self.tk = tk = widget.tk
-        w = widget._w
+        self._operations = {}
+        self.widget = widget            # widget instance
+        self.tk = tk = widget.tk        # widget's root
+        w = widget._w                   # widget's (full) Tk pathname
         self.orig = w + "_orig"
+        # Rename the Tcl command within Tcl:
         tk.call("rename", w, self.orig)
+        # Create a new Tcl command whose name is the widget's pathname, and
+        # whose action is to dispatch on the operation passed to the widget:
         tk.createcommand(w, self.dispatch)
 
     def __repr__(self):
@@ -19,74 +40,87 @@
                                              self.widget._w)
 
     def close(self):
-        for name in list(self.dict.keys()):
-            self.unregister(name)
+        for operation in list(self._operations):
+            self.unregister(operation)
         widget = self.widget; del self.widget
         orig = self.orig; del self.orig
         tk = widget.tk
         w = widget._w
         tk.deletecommand(w)
+        # restore the original widget Tcl command:
         tk.call("rename", orig, w)
 
-    def register(self, name, function):
-        if name in self.dict:
-            previous = dict[name]
-        else:
-            previous = OriginalCommand(self, name)
-        self.dict[name] = function
-        setattr(self.widget, name, function)
-        return previous
+    def register(self, operation, function):
+        self._operations[operation] = function
+        setattr(self.widget, operation, function)
+        return OriginalCommand(self, operation)
 
-    def unregister(self, name):
-        if name in self.dict:
-            function = self.dict[name]
-            del self.dict[name]
-            if hasattr(self.widget, name):
-                delattr(self.widget, name)
+    def unregister(self, operation):
+        if operation in self._operations:
+            function = self._operations[operation]
+            del self._operations[operation]
+            if hasattr(self.widget, operation):
+                delattr(self.widget, operation)
             return function
         else:
             return None
 
-    def dispatch(self, cmd, *args):
-        m = self.dict.get(cmd)
+    def dispatch(self, operation, *args):
+        '''Callback from Tcl which runs when the widget is referenced.
+
+        If an operation has been registered in self._operations, apply the
+        associated function to the args passed into Tcl. Otherwise, pass the
+        operation through to Tk via the original Tcl function.
+
+        Note that if a registered function is called, the operation is not
+        passed through to Tk.  Apply the function returned by self.register()
+        to *args to accomplish that.  For an example, see ColorDelegator.py.
+
+        '''
+        m = self._operations.get(operation)
         try:
             if m:
                 return m(*args)
             else:
-                return self.tk.call((self.orig, cmd) + args)
+                return self.tk.call((self.orig, operation) + args)
         except TclError:
             return ""
 
 
 class OriginalCommand:
 
-    def __init__(self, redir, name):
+    def __init__(self, redir, operation):
         self.redir = redir
-        self.name = name
+        self.operation = operation
         self.tk = redir.tk
         self.orig = redir.orig
         self.tk_call = self.tk.call
-        self.orig_and_name = (self.orig, self.name)
+        self.orig_and_operation = (self.orig, self.operation)
 
     def __repr__(self):
-        return "OriginalCommand(%r, %r)" % (self.redir, self.name)
+        return "OriginalCommand(%r, %r)" % (self.redir, self.operation)
 
     def __call__(self, *args):
-        return self.tk_call(self.orig_and_name + args)
+        return self.tk_call(self.orig_and_operation + args)
 
 
 def main():
     root = Tk()
+    root.wm_protocol("WM_DELETE_WINDOW", root.quit)
     text = Text()
     text.pack()
     text.focus_set()
     redir = WidgetRedirector(text)
-    global orig_insert
+    global previous_tcl_fcn
     def my_insert(*args):
         print("insert", args)
-        orig_insert(*args)
-    orig_insert = redir.register("insert", my_insert)
+        previous_tcl_fcn(*args)
+    previous_tcl_fcn = redir.register("insert", my_insert)
     root.mainloop()
+    redir.unregister("insert")  # runs after first 'close window'
+    redir.close()
+    root.mainloop()
+    root.destroy()
 
 if __name__ == "__main__":
     main()
diff --git a/Lib/idlelib/aboutDialog.py b/Lib/idlelib/aboutDialog.py
index e0a6558..43a1313 100644
--- a/Lib/idlelib/aboutDialog.py
+++ b/Lib/idlelib/aboutDialog.py
@@ -111,45 +111,31 @@
         idle_credits_b.pack(side=LEFT, padx=10, pady=10)
 
     def ShowLicense(self):
-        self.display_printer_text(license, 'About - License')
+        self.display_printer_text('About - License', license)
 
     def ShowCopyright(self):
-        self.display_printer_text(copyright, 'About - Copyright')
+        self.display_printer_text('About - Copyright', copyright)
 
     def ShowPythonCredits(self):
-        self.display_printer_text(credits, 'About - Python Credits')
+        self.display_printer_text('About - Python Credits', credits)
 
     def ShowIDLECredits(self):
-        self.ViewFile('About - Credits','CREDITS.txt')
+        self.display_file_text('About - Credits', 'CREDITS.txt', 'iso-8859-1')
 
     def ShowIDLEAbout(self):
-        self.ViewFile('About - Readme', 'README.txt')
+        self.display_file_text('About - Readme', 'README.txt')
 
     def ShowIDLENEWS(self):
-        self.ViewFile('About - NEWS', 'NEWS.txt')
+        self.display_file_text('About - NEWS', 'NEWS.txt')
 
-    def display_printer_text(self, printer, title):
+    def display_printer_text(self, title, printer):
         printer._Printer__setup()
-        data = '\n'.join(printer._Printer__lines)
-        textView.TextViewer(self, title, None, data)
+        text = '\n'.join(printer._Printer__lines)
+        textView.view_text(self, title, text)
 
-    def ViewFile(self, viewTitle, viewFile, encoding=None):
-        fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), viewFile)
-        if encoding:
-            import codecs
-            try:
-                textFile = codecs.open(fn, 'r')
-            except IOError:
-                import tkMessageBox
-                tkMessageBox.showerror(title='File Load Error',
-                                       message='Unable to load file %r .' % (fn,),
-                                       parent=self)
-                return
-            else:
-                data = textFile.read()
-        else:
-            data = None
-        textView.TextViewer(self, viewTitle, fn, data=data)
+    def display_file_text(self, title, filename, encoding=None):
+        fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)
+        textView.view_file(self, title, fn, encoding)
 
     def Ok(self, event=None):
         self.destroy()
diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py
index ad3071a..d00b579 100644
--- a/Lib/idlelib/configDialog.py
+++ b/Lib/idlelib/configDialog.py
@@ -15,7 +15,7 @@
 
 from idlelib.configHandler import idleConf
 from idlelib.dynOptionMenuWidget import DynOptionMenu
-from idlelib.tabpage import TabPageSet
+from idlelib.tabbedpages import TabbedPageSet
 from idlelib.keybindingDialog import GetKeysDialog
 from idlelib.configSectionNameDialog import GetCfgSectionNameDialog
 from idlelib.configHelpSourceEdit import GetHelpSourceDialog
@@ -24,6 +24,8 @@
 
     def __init__(self,parent,title):
         Toplevel.__init__(self, parent)
+        self.wm_withdraw()
+
         self.configure(borderwidth=5)
         self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
                 parent.winfo_rooty()+30))
@@ -58,31 +60,37 @@
         #self.bind('<F1>',self.Help) #context help
         self.LoadConfigs()
         self.AttachVarCallbacks() #avoid callbacks during LoadConfigs
+
+        self.wm_deiconify()
         self.wait_window()
 
     def CreateWidgets(self):
-        self.tabPages = TabPageSet(self,
-                pageNames=['Fonts/Tabs','Highlighting','Keys','General'])
-        self.tabPages.ChangePage()#activates default (first) page
-        frameActionButtons = Frame(self)
+        self.tabPages = TabbedPageSet(self,
+                page_names=['Fonts/Tabs','Highlighting','Keys','General'])
+        frameActionButtons = Frame(self,pady=2)
         #action buttons
         self.buttonHelp = Button(frameActionButtons,text='Help',
-                command=self.Help,takefocus=FALSE)
+                command=self.Help,takefocus=FALSE,
+                padx=6,pady=3)
         self.buttonOk = Button(frameActionButtons,text='Ok',
-                command=self.Ok,takefocus=FALSE)
+                command=self.Ok,takefocus=FALSE,
+                padx=6,pady=3)
         self.buttonApply = Button(frameActionButtons,text='Apply',
-                command=self.Apply,takefocus=FALSE)
+                command=self.Apply,takefocus=FALSE,
+                padx=6,pady=3)
         self.buttonCancel = Button(frameActionButtons,text='Cancel',
-                command=self.Cancel,takefocus=FALSE)
+                command=self.Cancel,takefocus=FALSE,
+                padx=6,pady=3)
         self.CreatePageFontTab()
         self.CreatePageHighlight()
         self.CreatePageKeys()
         self.CreatePageGeneral()
-        self.buttonHelp.pack(side=RIGHT,padx=5,pady=5)
-        self.buttonOk.pack(side=LEFT,padx=5,pady=5)
-        self.buttonApply.pack(side=LEFT,padx=5,pady=5)
-        self.buttonCancel.pack(side=LEFT,padx=5,pady=5)
+        self.buttonHelp.pack(side=RIGHT,padx=5)
+        self.buttonOk.pack(side=LEFT,padx=5)
+        self.buttonApply.pack(side=LEFT,padx=5)
+        self.buttonCancel.pack(side=LEFT,padx=5)
         frameActionButtons.pack(side=BOTTOM)
+        Frame(self, border=0).pack(side=BOTTOM,pady=2)
         self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH)
 
     def CreatePageFontTab(self):
@@ -94,16 +102,17 @@
         self.editFont=tkFont.Font(self,('courier',10,'normal'))
         ##widget creation
         #body frame
-        frame=self.tabPages.pages['Fonts/Tabs']['page']
+        frame=self.tabPages.pages['Fonts/Tabs'].frame
         #body section frames
-        frameFont=Frame(frame,borderwidth=2,relief=GROOVE)
-        frameIndent=Frame(frame,borderwidth=2,relief=GROOVE)
+        frameFont=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                             text=' Base Editor Font ')
+        frameIndent=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                               text=' Indentation Width ')
         #frameFont
-        labelFontTitle=Label(frameFont,text='Set Base Editor Font')
         frameFontName=Frame(frameFont)
         frameFontParam=Frame(frameFont)
         labelFontNameTitle=Label(frameFontName,justify=LEFT,
-                text='Font :')
+                text='Font Face :')
         self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE,
                 exportselection=FALSE)
         self.listFontName.bind('<ButtonRelease-1>',self.OnListFontButtonRelease)
@@ -124,14 +133,13 @@
         labelSpaceNumTitle=Label(frameIndentSize, justify=LEFT,
                                  text='Python Standard: 4 Spaces!')
         self.scaleSpaceNum=Scale(frameIndentSize, variable=self.spaceNum,
-                                 label='Indentation Width', orient='horizontal',
+                                 orient='horizontal',
                                  tickinterval=2, from_=2, to=16)
         #widget packing
         #body
-        frameFont.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
-        frameIndent.pack(side=LEFT,padx=5,pady=10,fill=Y)
+        frameFont.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
+        frameIndent.pack(side=LEFT,padx=5,pady=5,fill=Y)
         #frameFont
-        labelFontTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         frameFontName.pack(side=TOP,padx=5,pady=5,fill=X)
         frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X)
         labelFontNameTitle.pack(side=TOP,anchor=W)
@@ -143,7 +151,7 @@
         frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
         self.labelFontSample.pack(expand=TRUE,fill=BOTH)
         #frameIndent
-        frameIndentSize.pack(side=TOP,padx=5,pady=5,fill=BOTH)
+        frameIndentSize.pack(side=TOP,fill=X)
         labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5)
         self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X)
         return frame
@@ -158,10 +166,12 @@
         self.highlightTarget=StringVar(self)
         ##widget creation
         #body frame
-        frame=self.tabPages.pages['Highlighting']['page']
+        frame=self.tabPages.pages['Highlighting'].frame
         #body section frames
-        frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
-        frameTheme=Frame(frame,borderwidth=2,relief=GROOVE)
+        frameCustom=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                               text=' Custom Highlighting ')
+        frameTheme=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                              text=' Highlighting Theme ')
         #frameCustom
         self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
             font=('courier',12,''),cursor='hand2',width=21,height=10,
@@ -189,7 +199,6 @@
         text.config(state=DISABLED)
         self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1)
         frameFgBg=Frame(frameCustom)
-        labelCustomTitle=Label(frameCustom,text='Set Custom Highlighting')
         buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :',
             command=self.GetColour,highlightthickness=0)
         self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet,
@@ -202,7 +211,6 @@
         buttonSaveCustomTheme=Button(frameCustom,
             text='Save as New Custom Theme',command=self.SaveAsNewTheme)
         #frameTheme
-        labelThemeTitle=Label(frameTheme,text='Select a Highlighting Theme')
         labelTypeTitle=Label(frameTheme,text='Select : ')
         self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
             value=1,command=self.SetThemeType,text='a Built-in Theme')
@@ -216,10 +224,9 @@
                 command=self.DeleteCustomTheme)
         ##widget packing
         #body
-        frameCustom.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH)
-        frameTheme.pack(side=LEFT,padx=5,pady=10,fill=Y)
+        frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
+        frameTheme.pack(side=LEFT,padx=5,pady=5,fill=Y)
         #frameCustom
-        labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X)
         frameFgBg.pack(side=TOP,padx=5,pady=0)
         self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,
@@ -230,7 +237,6 @@
         self.radioBg.pack(side=RIGHT,anchor=W)
         buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5)
         #frameTheme
-        labelThemeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5)
         self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
@@ -248,13 +254,14 @@
         self.keyBinding=StringVar(self)
         ##widget creation
         #body frame
-        frame=self.tabPages.pages['Keys']['page']
+        frame=self.tabPages.pages['Keys'].frame
         #body section frames
-        frameCustom=Frame(frame,borderwidth=2,relief=GROOVE)
-        frameKeySets=Frame(frame,borderwidth=2,relief=GROOVE)
+        frameCustom=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                               text=' Custom Key Bindings ')
+        frameKeySets=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                           text=' Key Set ')
         #frameCustom
         frameTarget=Frame(frameCustom)
-        labelCustomTitle=Label(frameCustom,text='Set Custom Key Bindings')
         labelTargetTitle=Label(frameTarget,text='Action - Key(s)')
         scrollTargetY=Scrollbar(frameTarget)
         scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL)
@@ -270,7 +277,6 @@
         buttonSaveCustomKeys=Button(frameCustom,
                 text='Save as New Custom Key Set',command=self.SaveAsNewKeySet)
         #frameKeySets
-        labelKeysTitle=Label(frameKeySets,text='Select a Key Set')
         labelTypeTitle=Label(frameKeySets,text='Select : ')
         self.radioKeysBuiltin=Radiobutton(frameKeySets,variable=self.keysAreBuiltin,
             value=1,command=self.SetKeysType,text='a Built-in Key Set')
@@ -287,7 +293,6 @@
         frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
         frameKeySets.pack(side=LEFT,padx=5,pady=5,fill=Y)
         #frameCustom
-        labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         buttonSaveCustomKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
         self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
         frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
@@ -299,7 +304,6 @@
         scrollTargetY.grid(row=1,column=1,sticky=NS)
         scrollTargetX.grid(row=2,column=0,sticky=EW)
         #frameKeySets
-        labelKeysTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         self.radioKeysBuiltin.pack(side=TOP,anchor=W,padx=5)
         self.radioKeysCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
@@ -320,23 +324,24 @@
         self.helpBrowser=StringVar(self)
         #widget creation
         #body
-        frame=self.tabPages.pages['General']['page']
+        frame=self.tabPages.pages['General'].frame
         #body section frames
-        frameRun=Frame(frame,borderwidth=2,relief=GROOVE)
-        frameSave=Frame(frame,borderwidth=2,relief=GROOVE)
+        frameRun=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                            text=' Startup Preferences ')
+        frameSave=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                             text=' Autosave Preferences ')
         frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE)
         frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE)
         frameEncoding=Frame(frame,borderwidth=2,relief=GROOVE)
-        frameHelp=Frame(frame,borderwidth=2,relief=GROOVE)
+        frameHelp=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                             text=' Additional Help Sources ')
         #frameRun
-        labelRunTitle=Label(frameRun,text='Startup Preferences')
         labelRunChoiceTitle=Label(frameRun,text='At Startup')
         radioStartupEdit=Radiobutton(frameRun,variable=self.startupEdit,
             value=1,command=self.SetKeysType,text="Open Edit Window")
         radioStartupShell=Radiobutton(frameRun,variable=self.startupEdit,
             value=0,command=self.SetKeysType,text='Open Shell Window')
         #frameSave
-        labelSaveTitle=Label(frameSave,text='Autosave Preference')
         labelRunSaveTitle=Label(frameSave,text='At Start of Run (F5)  ')
         radioSaveAsk=Radiobutton(frameSave,variable=self.autoSave,
             value=0,command=self.SetKeysType,text="Prompt to Save")
@@ -367,7 +372,6 @@
         #frameHelp
         frameHelpList=Frame(frameHelp)
         frameHelpListButtons=Frame(frameHelpList)
-        labelHelpListTitle=Label(frameHelpList,text='Additional Help Sources:')
         scrollHelpList=Scrollbar(frameHelpList)
         self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE,
                 exportselection=FALSE)
@@ -389,12 +393,10 @@
         frameEncoding.pack(side=TOP,padx=5,pady=5,fill=X)
         frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
         #frameRun
-        labelRunTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
         radioStartupShell.pack(side=RIGHT,anchor=W,padx=5,pady=5)
         radioStartupEdit.pack(side=RIGHT,anchor=W,padx=5,pady=5)
         #frameSave
-        labelSaveTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
         labelRunSaveTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
         radioSaveAuto.pack(side=RIGHT,anchor=W,padx=5,pady=5)
         radioSaveAsk.pack(side=RIGHT,anchor=W,padx=5,pady=5)
@@ -415,7 +417,6 @@
         #frameHelp
         frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y)
         frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
-        labelHelpListTitle.pack(side=TOP,anchor=W)
         scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y)
         self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH)
         self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5)
@@ -1116,12 +1117,15 @@
     def ActivateConfigChanges(self):
         "Dynamically apply configuration changes"
         winInstances = self.parent.instance_dict.keys()
+        theme = idleConf.CurrentTheme()
+        cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
         for instance in winInstances:
             instance.ResetColorizer()
             instance.ResetFont()
             instance.set_notabs_indentwidth()
             instance.ApplyKeybindings()
             instance.reset_help_menu_entries()
+            instance.text.configure(insertbackground=cursor_color)
 
     def Cancel(self):
         self.destroy()
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 4ba5198..9fbe0e9 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -38,10 +38,11 @@
 
 # Thread shared globals: Establish a queue between a subthread (which handles
 # the socket) and the main thread (which runs user code), plus global
-# completion and exit flags:
+# completion, exit and interruptable (the main thread) flags:
 
 exit_now = False
 quitting = False
+interruptable = False
 
 def main(del_exitfunc=False):
     """Start the Python execution server in a subprocess
@@ -278,9 +279,14 @@
         self.autocomplete = AutoComplete.AutoComplete()
 
     def runcode(self, code):
+        global interruptable
         try:
             self.usr_exc_info = None
-            exec(code, self.locals)
+            interruptable = True
+            try:
+                exec(code, self.locals)
+            finally:
+                interruptable = False
         except:
             self.usr_exc_info = sys.exc_info()
             if quitting:
@@ -294,7 +300,8 @@
             flush_stdout()
 
     def interrupt_the_server(self):
-        thread.interrupt_main()
+        if interruptable:
+            thread.interrupt_main()
 
     def start_the_debugger(self, gui_adap_oid):
         return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
diff --git a/Lib/idlelib/tabbedpages.py b/Lib/idlelib/tabbedpages.py
new file mode 100644
index 0000000..98f6a27
--- /dev/null
+++ b/Lib/idlelib/tabbedpages.py
@@ -0,0 +1,478 @@
+"""An implementation of tabbed pages using only standard Tkinter.
+
+Originally developed for use in IDLE. Based on tabpage.py.
+
+Classes exported:
+TabbedPageSet -- A Tkinter implementation of a tabbed-page widget.
+TabBarSet -- A widget containing tabs (buttons) in one or more rows.
+
+"""
+from Tkinter import *
+
+class InvalidNameError(Exception): pass
+class AlreadyExistsError(Exception): pass
+
+
+class TabBarSet(Frame):
+    """A widget containing tabs (buttons) in one or more rows.
+
+    Only one tab may be selected at a time.
+
+    """
+    def __init__(self, page_set, select_command,
+                 tabs=None, n_rows=1, max_tabs_per_row=5,
+                 expand_tabs=False, **kw):
+        """Constructor arguments:
+
+        select_command -- A callable which will be called when a tab is
+        selected. It is called with the name of the selected tab as an
+        argument.
+
+        tabs -- A list of strings, the names of the tabs. Should be specified in
+        the desired tab order. The first tab will be the default and first
+        active tab. If tabs is None or empty, the TabBarSet will be initialized
+        empty.
+
+        n_rows -- Number of rows of tabs to be shown. If n_rows <= 0 or is
+        None, then the number of rows will be decided by TabBarSet. See
+        _arrange_tabs() for details.
+
+        max_tabs_per_row -- Used for deciding how many rows of tabs are needed,
+        when the number of rows is not constant. See _arrange_tabs() for
+        details.
+
+        """
+        Frame.__init__(self, page_set, **kw)
+        self.select_command = select_command
+        self.n_rows = n_rows
+        self.max_tabs_per_row = max_tabs_per_row
+        self.expand_tabs = expand_tabs
+        self.page_set = page_set
+
+        self._tabs = {}
+        self._tab2row = {}
+        if tabs:
+            self._tab_names = list(tabs)
+        else:
+            self._tab_names = []
+        self._selected_tab = None
+        self._tab_rows = []
+
+        self.padding_frame = Frame(self, height=2,
+                                   borderwidth=0, relief=FLAT,
+                                   background=self.cget('background'))
+        self.padding_frame.pack(side=TOP, fill=X, expand=False)
+
+        self._arrange_tabs()
+
+    def add_tab(self, tab_name):
+        """Add a new tab with the name given in tab_name."""
+        if not tab_name:
+            raise InvalidNameError("Invalid Tab name: '%s'" % tab_name)
+        if tab_name in self._tab_names:
+            raise AlreadyExistsError("Tab named '%s' already exists" %tab_name)
+
+        self._tab_names.append(tab_name)
+        self._arrange_tabs()
+
+    def remove_tab(self, tab_name):
+        """Remove the tab with the name given in tab_name."""
+        if not tab_name in self._tab_names:
+            raise KeyError("No such Tab: '%s" % page_name)
+
+        self._tab_names.remove(tab_name)
+        self._arrange_tabs()
+
+    def select_tab(self, tab_name):
+        """Select the tab with the name given in tab_name."""
+        if tab_name == self._selected_tab:
+            return
+        if tab_name is not None and tab_name not in self._tabs:
+            raise KeyError("No such Tab: '%s" % page_name)
+
+        # deselect the current selected tab
+        if self._selected_tab is not None:
+            self._tabs[self._selected_tab].set_normal()
+        self._selected_tab = None
+
+        if tab_name is not None:
+            # activate the tab named tab_name
+            self._selected_tab = tab_name
+            tab = self._tabs[tab_name]
+            tab.set_selected()
+            # move the tab row with the selected tab to the bottom
+            tab_row = self._tab2row[tab]
+            tab_row.pack_forget()
+            tab_row.pack(side=TOP, fill=X, expand=0)
+
+    def _add_tab_row(self, tab_names, expand_tabs):
+        if not tab_names:
+            return
+
+        tab_row = Frame(self)
+        tab_row.pack(side=TOP, fill=X, expand=0)
+        tab_row.tab_set = self
+        self._tab_rows.append(tab_row)
+
+        for tab_name in tab_names:
+            def tab_command(select_command=self.select_command,
+                            tab_name=tab_name):
+                return select_command(tab_name)
+            tab = TabBarSet.TabButton(tab_row, tab_name, tab_command)
+            if expand_tabs:
+                tab.pack(side=LEFT, fill=X, expand=True)
+            else:
+                tab.pack(side=LEFT)
+            self._tabs[tab_name] = tab
+            self._tab2row[tab] = tab_row
+
+        tab.is_last_in_row = True
+
+    def _reset_tab_rows(self):
+        while self._tab_rows:
+            tab_row = self._tab_rows.pop()
+            tab_row.destroy()
+        self._tab2row = {}
+
+    def _arrange_tabs(self):
+        """
+        Arrange the tabs in rows, in the order in which they were added.
+
+        If n_rows >= 1, this will be the number of rows used. Otherwise the
+        number of rows will be calculated according to the number of tabs and
+        max_tabs_per_row. In this case, the number of rows may change when
+        adding/removing tabs.
+
+        """
+        # remove all tabs and rows
+        for tab_name in self._tabs.keys():
+            self._tabs.pop(tab_name).destroy()
+        self._reset_tab_rows()
+
+        if not self._tab_names:
+            return
+
+        if self.n_rows is not None and self.n_rows > 0:
+            n_rows = self.n_rows
+        else:
+            # calculate the required number of rows
+            n_rows = (len(self._tab_names) - 1) // self.max_tabs_per_row + 1
+
+        i = 0
+        expand_tabs = self.expand_tabs or n_rows > 1
+        for row_index in xrange(n_rows):
+            # calculate required number of tabs in this row
+            n_tabs = (len(self._tab_names) - i - 1) // (n_rows - row_index) + 1
+            tab_names = self._tab_names[i:i + n_tabs]
+            i += n_tabs
+            self._add_tab_row(tab_names, expand_tabs)
+
+        # re-select selected tab so it is properly displayed
+        selected = self._selected_tab
+        self.select_tab(None)
+        if selected in self._tab_names:
+            self.select_tab(selected)
+
+    class TabButton(Frame):
+        """A simple tab-like widget."""
+
+        bw = 2 # borderwidth
+
+        def __init__(self, tab_row, name, command):
+            """Constructor arguments:
+
+            name -- The tab's name, which will appear in its button.
+
+            command -- The command to be called upon selection of the tab. It
+            is called with the tab's name as an argument.
+
+            """
+            Frame.__init__(self, tab_row, borderwidth=self.bw)
+            self.button = Radiobutton(self, text=name, command=command,
+                padx=5, pady=1, takefocus=FALSE, indicatoron=FALSE,
+                highlightthickness=0, selectcolor='', borderwidth=0)
+            self.button.pack(side=LEFT, fill=X, expand=True)
+
+            self.tab_set = tab_row.tab_set
+
+            self.is_last_in_row = False
+
+            self._init_masks()
+            self.set_normal()
+
+        def set_selected(self):
+            """Assume selected look"""
+            for widget in self, self.mskl.ml, self.mskr.mr:
+                widget.config(relief=RAISED)
+            self._place_masks(selected=True)
+
+        def set_normal(self):
+            """Assume normal look"""
+            for widget in self, self.mskl.ml, self.mskr.mr:
+                widget.config(relief=RAISED)
+            self._place_masks(selected=False)
+
+        def _init_masks(self):
+            page_set = self.tab_set.page_set
+            background = page_set.pages_frame.cget('background')
+            # mask replaces the middle of the border with the background color
+            self.mask = Frame(page_set, borderwidth=0, relief=FLAT,
+                              background=background)
+            # mskl replaces the bottom-left corner of the border with a normal
+            # left border
+            self.mskl = Frame(page_set, borderwidth=0, relief=FLAT,
+                              background=background)
+            self.mskl.ml = Frame(self.mskl, borderwidth=self.bw,
+                                 relief=RAISED)
+            self.mskl.ml.place(x=0, y=-self.bw,
+                               width=2*self.bw, height=self.bw*4)
+            # mskr replaces the bottom-right corner of the border with a normal
+            # right border
+            self.mskr = Frame(page_set, borderwidth=0, relief=FLAT,
+                              background=background)
+            self.mskr.mr = Frame(self.mskr, borderwidth=self.bw,
+                                 relief=RAISED)
+
+        def _place_masks(self, selected=False):
+            height = self.bw
+            if selected:
+                height += self.bw
+
+            self.mask.place(in_=self,
+                            relx=0.0, x=0,
+                            rely=1.0, y=0,
+                            relwidth=1.0, width=0,
+                            relheight=0.0, height=height)
+
+            self.mskl.place(in_=self,
+                            relx=0.0, x=-self.bw,
+                            rely=1.0, y=0,
+                            relwidth=0.0, width=self.bw,
+                            relheight=0.0, height=height)
+
+            page_set = self.tab_set.page_set
+            if selected and ((not self.is_last_in_row) or
+                             (self.winfo_rootx() + self.winfo_width() <
+                              page_set.winfo_rootx() + page_set.winfo_width())
+                             ):
+                # for a selected tab, if its rightmost edge isn't on the
+                # rightmost edge of the page set, the right mask should be one
+                # borderwidth shorter (vertically)
+                height -= self.bw
+
+            self.mskr.place(in_=self,
+                            relx=1.0, x=0,
+                            rely=1.0, y=0,
+                            relwidth=0.0, width=self.bw,
+                            relheight=0.0, height=height)
+
+            self.mskr.mr.place(x=-self.bw, y=-self.bw,
+                               width=2*self.bw, height=height + self.bw*2)
+
+            # finally, lower the tab set so that all of the frames we just
+            # placed hide it
+            self.tab_set.lower()
+
+class TabbedPageSet(Frame):
+    """A Tkinter tabbed-pane widget.
+
+    Constains set of 'pages' (or 'panes') with tabs above for selecting which
+    page is displayed. Only one page will be displayed at a time.
+
+    Pages may be accessed through the 'pages' attribute, which is a dictionary
+    of pages, using the name given as the key. A page is an instance of a
+    subclass of Tk's Frame widget.
+
+    The page widgets will be created (and destroyed when required) by the
+    TabbedPageSet. Do not call the page's pack/place/grid/destroy methods.
+
+    Pages may be added or removed at any time using the add_page() and
+    remove_page() methods.
+
+    """
+    class Page(object):
+        """Abstract base class for TabbedPageSet's pages.
+
+        Subclasses must override the _show() and _hide() methods.
+
+        """
+        uses_grid = False
+
+        def __init__(self, page_set):
+            self.frame = Frame(page_set, borderwidth=2, relief=RAISED)
+
+        def _show(self):
+            raise NotImplementedError
+
+        def _hide(self):
+            raise NotImplementedError
+
+    class PageRemove(Page):
+        """Page class using the grid placement manager's "remove" mechanism."""
+        uses_grid = True
+
+        def _show(self):
+            self.frame.grid(row=0, column=0, sticky=NSEW)
+
+        def _hide(self):
+            self.frame.grid_remove()
+
+    class PageLift(Page):
+        """Page class using the grid placement manager's "lift" mechanism."""
+        uses_grid = True
+
+        def __init__(self, page_set):
+            super(TabbedPageSet.PageLift, self).__init__(page_set)
+            self.frame.grid(row=0, column=0, sticky=NSEW)
+            self.frame.lower()
+
+        def _show(self):
+            self.frame.lift()
+
+        def _hide(self):
+            self.frame.lower()
+
+    class PagePackForget(Page):
+        """Page class using the pack placement manager's "forget" mechanism."""
+        def _show(self):
+            self.frame.pack(fill=BOTH, expand=True)
+
+        def _hide(self):
+            self.frame.pack_forget()
+
+    def __init__(self, parent, page_names=None, page_class=PageLift,
+                 n_rows=1, max_tabs_per_row=5, expand_tabs=False,
+                 **kw):
+        """Constructor arguments:
+
+        page_names -- A list of strings, each will be the dictionary key to a
+        page's widget, and the name displayed on the page's tab. Should be
+        specified in the desired page order. The first page will be the default
+        and first active page. If page_names is None or empty, the
+        TabbedPageSet will be initialized empty.
+
+        n_rows, max_tabs_per_row -- Parameters for the TabBarSet which will
+        manage the tabs. See TabBarSet's docs for details.
+
+        page_class -- Pages can be shown/hidden using three mechanisms:
+
+        * PageLift - All pages will be rendered one on top of the other. When
+          a page is selected, it will be brought to the top, thus hiding all
+          other pages. Using this method, the TabbedPageSet will not be resized
+          when pages are switched. (It may still be resized when pages are
+          added/removed.)
+
+        * PageRemove - When a page is selected, the currently showing page is
+          hidden, and the new page shown in its place. Using this method, the
+          TabbedPageSet may resize when pages are changed.
+
+        * PagePackForget - This mechanism uses the pack placement manager.
+          When a page is shown it is packed, and when it is hidden it is
+          unpacked (i.e. pack_forget). This mechanism may also cause the
+          TabbedPageSet to resize when the page is changed.
+
+        """
+        Frame.__init__(self, parent, kw)
+
+        self.page_class = page_class
+        self.pages = {}
+        self._pages_order = []
+        self._current_page = None
+        self._default_page = None
+
+        self.columnconfigure(0, weight=1)
+        self.rowconfigure(1, weight=1)
+
+        self.pages_frame = Frame(self)
+        self.pages_frame.grid(row=1, column=0, sticky=NSEW)
+        if self.page_class.uses_grid:
+            self.pages_frame.columnconfigure(0, weight=1)
+            self.pages_frame.rowconfigure(0, weight=1)
+
+        # the order of the following commands is important
+        self._tab_set = TabBarSet(self, self.change_page, n_rows=n_rows,
+                                  max_tabs_per_row=max_tabs_per_row,
+                                  expand_tabs=expand_tabs)
+        if page_names:
+            for name in page_names:
+                self.add_page(name)
+        self._tab_set.grid(row=0, column=0, sticky=NSEW)
+
+        self.change_page(self._default_page)
+
+    def add_page(self, page_name):
+        """Add a new page with the name given in page_name."""
+        if not page_name:
+            raise InvalidNameError("Invalid TabPage name: '%s'" % page_name)
+        if page_name in self.pages:
+            raise AlreadyExistsError(
+                "TabPage named '%s' already exists" % page_name)
+
+        self.pages[page_name] = self.page_class(self.pages_frame)
+        self._pages_order.append(page_name)
+        self._tab_set.add_tab(page_name)
+
+        if len(self.pages) == 1: # adding first page
+            self._default_page = page_name
+            self.change_page(page_name)
+
+    def remove_page(self, page_name):
+        """Destroy the page whose name is given in page_name."""
+        if not page_name in self.pages:
+            raise KeyError("No such TabPage: '%s" % page_name)
+
+        self._pages_order.remove(page_name)
+
+        # handle removing last remaining, default, or currently shown page
+        if len(self._pages_order) > 0:
+            if page_name == self._default_page:
+                # set a new default page
+                self._default_page = self._pages_order[0]
+        else:
+            self._default_page = None
+
+        if page_name == self._current_page:
+            self.change_page(self._default_page)
+
+        self._tab_set.remove_tab(page_name)
+        page = self.pages.pop(page_name)
+        page.frame.destroy()
+
+    def change_page(self, page_name):
+        """Show the page whose name is given in page_name."""
+        if self._current_page == page_name:
+            return
+        if page_name is not None and page_name not in self.pages:
+            raise KeyError("No such TabPage: '%s'" % page_name)
+
+        if self._current_page is not None:
+            self.pages[self._current_page]._hide()
+        self._current_page = None
+
+        if page_name is not None:
+            self._current_page = page_name
+            self.pages[page_name]._show()
+
+        self._tab_set.select_tab(page_name)
+
+if __name__ == '__main__':
+    # test dialog
+    root=Tk()
+    tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0,
+                          expand_tabs=False,
+                          )
+    tabPage.pack(side=TOP, expand=TRUE, fill=BOTH)
+    Label(tabPage.pages['Foobar'].frame, text='Foo', pady=20).pack()
+    Label(tabPage.pages['Foobar'].frame, text='Bar', pady=20).pack()
+    Label(tabPage.pages['Baz'].frame, text='Baz').pack()
+    entryPgName=Entry(root)
+    buttonAdd=Button(root, text='Add Page',
+            command=lambda:tabPage.add_page(entryPgName.get()))
+    buttonRemove=Button(root, text='Remove Page',
+            command=lambda:tabPage.remove_page(entryPgName.get()))
+    labelPgName=Label(root, text='name of page to add/remove:')
+    buttonAdd.pack(padx=5, pady=5)
+    buttonRemove.pack(padx=5, pady=5)
+    labelPgName.pack(padx=5)
+    entryPgName.pack(padx=5)
+    root.mainloop()
diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py
index 917a6cc..0e7e663 100644
--- a/Lib/idlelib/textView.py
+++ b/Lib/idlelib/textView.py
@@ -6,13 +6,12 @@
 import tkMessageBox
 
 class TextViewer(Toplevel):
-    """
-    simple text viewer dialog for idle
-    """
-    def __init__(self, parent, title, fileName, data=None):
-        """If data exists, load it into viewer, otherwise try to load file.
+    """A simple text viewer dialog for IDLE
 
-        fileName - string, should be an absoulute filename
+    """
+    def __init__(self, parent, title, text):
+        """Show the given text in a scrollable window with a 'close' button
+
         """
         Toplevel.__init__(self, parent)
         self.configure(borderwidth=5)
@@ -33,23 +32,10 @@
         #key bindings for this dialog
         self.bind('<Return>',self.Ok) #dismiss dialog
         self.bind('<Escape>',self.Ok) #dismiss dialog
-        if data:
-            self.textView.insert(0.0, data)
-        else:
-            self.LoadTextFile(fileName)
+        self.textView.insert(0.0, text)
         self.textView.config(state=DISABLED)
         self.wait_window()
 
-    def LoadTextFile(self, fileName):
-        textFile = None
-        try:
-            textFile = open(fileName, 'r')
-        except IOError:
-            tkMessageBox.showerror(title='File Load Error',
-                    message='Unable to load file %r .' % (fileName,))
-        else:
-            self.textView.insert(0.0,textFile.read())
-
     def CreateWidgets(self):
         frameText = Frame(self, relief=SUNKEN, height=700)
         frameButtons = Frame(self)
@@ -70,9 +56,38 @@
     def Ok(self, event=None):
         self.destroy()
 
+
+def view_text(parent, title, text):
+    TextViewer(parent, title, text)
+
+def view_file(parent, title, filename, encoding=None):
+    try:
+        if encoding:
+            import codecs
+            textFile = codecs.open(filename, 'r')
+        else:
+            textFile = open(filename, 'r')
+    except IOError:
+        import tkMessageBox
+        tkMessageBox.showerror(title='File Load Error',
+                               message='Unable to load file %r .' % filename,
+                               parent=parent)
+    else:
+        return view_text(parent, title, textFile.read())
+
+
 if __name__ == '__main__':
     #test the dialog
     root=Tk()
-    Button(root,text='View',
-            command=lambda:TextViewer(root,'Text','./textView.py')).pack()
+    root.title('textView test')
+    filename = './textView.py'
+    text = file(filename, 'r').read()
+    btn1 = Button(root, text='view_text',
+                 command=lambda:view_text(root, 'view_text', text))
+    btn1.pack(side=LEFT)
+    btn2 = Button(root, text='view_file',
+                  command=lambda:view_file(root, 'view_file', filename))
+    btn2.pack(side=LEFT)
+    close = Button(root, text='Close', command=root.destroy)
+    close.pack(side=RIGHT)
     root.mainloop()