From buildbot at python.org Fri Aug 1 00:21:26 2008 From: buildbot at python.org (buildbot at python.org) Date: Thu, 31 Jul 2008 22:21:26 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo trunk Message-ID: <20080731222126.C58A81E4005@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%20trunk/builds/4006 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-x86 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_signal.py", line 165, in test_main pickle.dump(None, done_w) File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/contextlib.py", line 153, in __exit__ self.thing.close() IOError: [Errno 9] Bad file descriptor 1 test failed: test_signal make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 01:04:26 2008 From: buildbot at python.org (buildbot at python.org) Date: Thu, 31 Jul 2008 23:04:26 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian trunk Message-ID: <20080731230426.E00171E4005@bag.python.org> The Buildbot has detected a new failure of S-390 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%20trunk/builds/922 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_pickletools make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 01:13:32 2008 From: buildbot at python.org (buildbot at python.org) Date: Thu, 31 Jul 2008 23:13:32 +0000 Subject: [Python-checkins] buildbot failure in PPC64 Debian trunk Message-ID: <20080731231332.341711E400F@bag.python.org> The Buildbot has detected a new failure of PPC64 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/PPC64%20Debian%20trunk/builds/1254 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_pickletools make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Fri Aug 1 01:39:06 2008 From: python-checkins at python.org (amaury.forgeotdarc) Date: Fri, 1 Aug 2008 01:39:06 +0200 (CEST) Subject: [Python-checkins] r65342 - in python/trunk: Lib/test/test_unicode.py Objects/unicodeobject.c Message-ID: <20080731233906.0B7601E4005@bag.python.org> Author: amaury.forgeotdarc Date: Fri Aug 1 01:39:05 2008 New Revision: 65342 Log: Correct a crash when two successive unicode allocations fail with a MemoryError: the freelist contained half-initialized objects with freed pointers. The comment /* XXX UNREF/NEWREF interface should be more symmetrical */ was copied from tupleobject.c, and appears in some other places. I sign the petition. Modified: python/trunk/Lib/test/test_unicode.py python/trunk/Objects/unicodeobject.c Modified: python/trunk/Lib/test/test_unicode.py ============================================================================== --- python/trunk/Lib/test/test_unicode.py (original) +++ python/trunk/Lib/test/test_unicode.py Fri Aug 1 01:39:05 2008 @@ -1113,6 +1113,20 @@ # will fail self.assertRaises(UnicodeEncodeError, "foo{0}".format, u'\u1000bar') + def test_raiseMemError(self): + # Ensure that the freelist contains a consistent object, even + # when a string allocation fails with a MemoryError. + # This used to crash the interpreter, + # or leak references when the number was smaller. + try: + u"a" * (sys.maxint // 2 - 100) + except MemoryError: + pass + try: + u"a" * (sys.maxint // 2 - 100) + except MemoryError: + pass + def test_main(): test_support.run_unittest(__name__) Modified: python/trunk/Objects/unicodeobject.c ============================================================================== --- python/trunk/Objects/unicodeobject.c (original) +++ python/trunk/Objects/unicodeobject.c Fri Aug 1 01:39:05 2008 @@ -315,7 +315,7 @@ if ((unicode->length < length) && unicode_resize(unicode, length) < 0) { PyObject_DEL(unicode->str); - goto onError; + unicode->str = NULL; } } else { @@ -352,6 +352,8 @@ return unicode; onError: + /* XXX UNREF/NEWREF interface should be more symmetrical */ + _Py_DEC_REFTOTAL; _Py_ForgetReference((PyObject *)unicode); PyObject_Del(unicode); return NULL; From buildbot at python.org Fri Aug 1 01:53:12 2008 From: buildbot at python.org (buildbot at python.org) Date: Thu, 31 Jul 2008 23:53:12 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable 3.0 Message-ID: <20080731235312.99C1E1E4005@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%203.0/builds/1281 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_smtplib test_timeout make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 02:08:43 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 00:08:43 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo trunk Message-ID: <20080801000843.8A73F1E4005@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/1250 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_pickletools make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 02:54:55 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 00:54:55 +0000 Subject: [Python-checkins] buildbot failure in x86 osx.5 3.0 Message-ID: <20080801005456.834DA1E4005@bag.python.org> The Buildbot has detected a new failure of x86 osx.5 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20osx.5%203.0/builds/92 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-x86-osx5 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_calendar test_email make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Fri Aug 1 03:21:50 2008 From: python-checkins at python.org (brett.cannon) Date: Fri, 1 Aug 2008 03:21:50 +0200 (CEST) Subject: [Python-checkins] r65346 - python/trunk/Lib/traceback.py Message-ID: <20080801012150.E0A451E4005@bag.python.org> Author: brett.cannon Date: Fri Aug 1 03:21:50 2008 New Revision: 65346 Log: Fix a DeprecationWarning about __getitem__() and exceptions in the 'traceback' module. Modified: python/trunk/Lib/traceback.py Modified: python/trunk/Lib/traceback.py ============================================================================== --- python/trunk/Lib/traceback.py (original) +++ python/trunk/Lib/traceback.py Fri Aug 1 03:21:50 2008 @@ -181,7 +181,7 @@ # It was a syntax error; show exactly where the problem was found. lines = [] try: - msg, (filename, lineno, offset, badline) = value + msg, (filename, lineno, offset, badline) = value.args except Exception: pass else: From buildbot at python.org Fri Aug 1 03:23:56 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 01:23:56 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 3.0 Message-ID: <20080801012356.8BA061E4005@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%203.0/builds/77 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 03:31:14 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 01:31:14 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080801013114.926851E400D@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/860 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: amaury.forgeotdarc BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_xmlrpc_net make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Fri Aug 1 03:31:32 2008 From: python-checkins at python.org (jesse.noller) Date: Fri, 1 Aug 2008 03:31:32 +0200 (CEST) Subject: [Python-checkins] r65348 - peps/trunk/pep-0371.txt Message-ID: <20080801013132.E03D31E4017@bag.python.org> Author: jesse.noller Date: Fri Aug 1 03:31:32 2008 New Revision: 65348 Log: Adjust threading article date, shift open issues to closed for pep 371 Modified: peps/trunk/pep-0371.txt Modified: peps/trunk/pep-0371.txt ============================================================================== --- peps/trunk/pep-0371.txt (original) +++ peps/trunk/pep-0371.txt Fri Aug 1 03:31:32 2008 @@ -357,16 +357,6 @@ Open Issues - * All existing tests for the package should be converted to - UnitTest format. - - * Existing documentation has to be moved to ReST formatting. - - * Verify code coverage percentage of existing test suite. - - * Verify current source tree conforms to standard library - practices. - * Confirm no "default" remote connection capabilities, if needed enable the remote security mechanisms by default for those classes which offer remote capabilities. @@ -375,10 +365,12 @@ either need to be added, or the reason for their exclusion needs to be identified and documented clearly. - * The PyGILState bug patch submitted in issue 1683 by roudkerk +Closed Issues + + * The PyGILState bug patch submitted in issue 1683 by roudkerk must be applied for the package unit tests to work. -Closed Issues + * Existing documentation has to be moved to ReST formatting. * Reliance on ctypes: The pyprocessing package's reliance on ctypes prevents the package from functioning on platforms where @@ -415,7 +407,7 @@ http://wiki.python.org/moin/ParallelProcessing [6] The original run_benchmark.py code was published in Python - Magazine in December 2008: "Python Threads and the Global + Magazine in December 2007: "Python Threads and the Global Interpreter Lock" by Jesse Noller. It has been modified for this PEP. From python-checkins at python.org Fri Aug 1 03:34:05 2008 From: python-checkins at python.org (brett.cannon) Date: Fri, 1 Aug 2008 03:34:05 +0200 (CEST) Subject: [Python-checkins] r65349 - python/trunk/Lib/textwrap.py Message-ID: <20080801013405.850C31E4005@bag.python.org> Author: brett.cannon Date: Fri Aug 1 03:34:05 2008 New Revision: 65349 Log: Remove assignment to True/False to silence the SyntaxWarning that is triggered by -3. Modified: python/trunk/Lib/textwrap.py Modified: python/trunk/Lib/textwrap.py ============================================================================== --- python/trunk/Lib/textwrap.py (original) +++ python/trunk/Lib/textwrap.py Fri Aug 1 03:34:05 2008 @@ -11,11 +11,11 @@ # Do the right thing with boolean values for all known Python versions # (so this module can be copied to projects that don't depend on Python -# 2.3, e.g. Optik and Docutils). -try: - True, False -except NameError: - (True, False) = (1, 0) +# 2.3, e.g. Optik and Docutils) by uncommenting the block of code below. +#try: +# True, False +#except NameError: +# (True, False) = (1, 0) __all__ = ['TextWrapper', 'wrap', 'fill'] From python-checkins at python.org Fri Aug 1 03:36:47 2008 From: python-checkins at python.org (brett.cannon) Date: Fri, 1 Aug 2008 03:36:47 +0200 (CEST) Subject: [Python-checkins] r65351 - in python/trunk/Lib: bdb.py pdb.py Message-ID: <20080801013647.E6F031E4005@bag.python.org> Author: brett.cannon Date: Fri Aug 1 03:36:47 2008 New Revision: 65351 Log: Remove use of tuple unpacking so as to silence SyntaxWarning as triggered by -3. Modified: python/trunk/Lib/bdb.py python/trunk/Lib/pdb.py Modified: python/trunk/Lib/bdb.py ============================================================================== --- python/trunk/Lib/bdb.py (original) +++ python/trunk/Lib/bdb.py Fri Aug 1 03:36:47 2008 @@ -131,8 +131,7 @@ raise NotImplementedError, "subclass of bdb must implement do_clear()" def break_anywhere(self, frame): - return self.breaks.has_key( - self.canonic(frame.f_code.co_filename)) + return self.canonic(frame.f_code.co_filename) in self.breaks # Derived classes should override the user_* methods # to gain control. @@ -150,7 +149,8 @@ """This method is called when a return trap is set here.""" pass - def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): + def user_exception(self, frame, exc_info): + exc_type, exc_value, exc_traceback = exc_info """This method is called if an exception occurs, but only if we are to stop at or just below this level.""" pass Modified: python/trunk/Lib/pdb.py ============================================================================== --- python/trunk/Lib/pdb.py (original) +++ python/trunk/Lib/pdb.py Fri Aug 1 03:36:47 2008 @@ -175,7 +175,8 @@ print >>self.stdout, '--Return--' self.interaction(frame, None) - def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): + def user_exception(self, frame, exc_info): + exc_type, exc_value, exc_traceback = exc_info """This function is called if an exception occurs, but only if we are to stop at or just below this level.""" frame.f_locals['__exception__'] = exc_type, exc_value From python-checkins at python.org Fri Aug 1 03:38:17 2008 From: python-checkins at python.org (brett.cannon) Date: Fri, 1 Aug 2008 03:38:17 +0200 (CEST) Subject: [Python-checkins] r65351 - svn:log Message-ID: <20080801013817.CAD0D1E4005@bag.python.org> Author: brett.cannon Revision: 65351 Property Name: svn:log Action: modified Property diff: --- old property value +++ new property value @@ -1,2 +1,2 @@ -Remove use of tuple unpacking so as to silence SyntaxWarning as triggered by --3. +Remove use of tuple unpacking and dict.has_key() so as to silence +SyntaxWarning as triggered by -3. From python-checkins at python.org Fri Aug 1 03:40:25 2008 From: python-checkins at python.org (brett.cannon) Date: Fri, 1 Aug 2008 03:40:25 +0200 (CEST) Subject: [Python-checkins] r65353 - python/trunk/Lib/inspect.py Message-ID: <20080801014025.443801E4005@bag.python.org> Author: brett.cannon Date: Fri Aug 1 03:40:24 2008 New Revision: 65353 Log: Silence (Syntax|Deprecation)Warning for 'inspect'. Had to remove tuple unpacking in a parameter list and set some constants by hand that were pulled from the 'compiler' package. Modified: python/trunk/Lib/inspect.py Modified: python/trunk/Lib/inspect.py ============================================================================== --- python/trunk/Lib/inspect.py (original) +++ python/trunk/Lib/inspect.py Fri Aug 1 03:40:24 2008 @@ -41,9 +41,10 @@ from abc import ABCMeta from operator import attrgetter from collections import namedtuple -from compiler.consts import (CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, - CO_VARKEYWORDS, CO_GENERATOR) +# These constants are from Include/code.h. +CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 0x1, 0x2, 0x4, 0x8 +CO_NESTED, CO_GENERATOR, CO_NOFREE = 0x10, 0x20, 0x40 # See Include/object.h TPFLAGS_IS_ABSTRACT = 1 << 20 @@ -428,8 +429,9 @@ def getmoduleinfo(path): """Get the module name, suffix, mode, and module type for a given file.""" filename = os.path.basename(path) - suffixes = map(lambda (suffix, mode, mtype): - (-len(suffix), suffix, mode, mtype), imp.get_suffixes()) + suffixes = map(lambda info: + (-len(info[0]), info[0], info[1], info[2]), + imp.get_suffixes()) suffixes.sort() # try longest suffixes first, in case they overlap for neglen, suffix, mode, mtype in suffixes: if filename[neglen:] == suffix: @@ -630,7 +632,9 @@ self.passline = False self.last = 1 - def tokeneater(self, type, token, (srow, scol), (erow, ecol), line): + def tokeneater(self, type, token, srow_scol, erow_ecol, line): + srow, scol = srow_scol + erow, ecol = erow_ecol if not self.started: # look for the first "def", "class" or "lambda" if token in ("def", "class", "lambda"): From python-checkins at python.org Fri Aug 1 03:45:49 2008 From: python-checkins at python.org (brett.cannon) Date: Fri, 1 Aug 2008 03:45:49 +0200 (CEST) Subject: [Python-checkins] r65355 - python/trunk/Lib/rlcompleter.py Message-ID: <20080801014549.9B7D81E4005@bag.python.org> Author: brett.cannon Date: Fri Aug 1 03:45:49 2008 New Revision: 65355 Log: Remove a use of callable() to silence the warning triggered under -3. Modified: python/trunk/Lib/rlcompleter.py Modified: python/trunk/Lib/rlcompleter.py ============================================================================== --- python/trunk/Lib/rlcompleter.py (original) +++ python/trunk/Lib/rlcompleter.py Fri Aug 1 03:45:49 2008 @@ -93,7 +93,7 @@ return None def _callable_postfix(self, val, word): - if callable(val): + if hasattr(val, '__call__'): word = word + "(" return word From buildbot at python.org Fri Aug 1 04:10:39 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 02:10:39 +0000 Subject: [Python-checkins] buildbot failure in OS X x86 3.0 Message-ID: <20080801021039.6C3B11E4005@bag.python.org> The Buildbot has detected a new failure of OS X x86 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/OS%20X%20x86%203.0/builds/117 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: noller-osx86 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_multiprocessing make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Fri Aug 1 04:14:30 2008 From: python-checkins at python.org (guilherme.polo) Date: Fri, 1 Aug 2008 04:14:30 +0200 (CEST) Subject: [Python-checkins] r65356 - in sandbox/trunk/ttk-gsoc/src/idlelib: EditorWindow.py FileList.py PyShell.py editorpage.py tabbedpages.py Message-ID: <20080801021430.84A5C1E4005@bag.python.org> Author: guilherme.polo Date: Fri Aug 1 04:14:29 2008 New Revision: 65356 Log: FileList: canonize method is now just a function, and marked it as private; Changed filename_changed_edit signature, first step towards working with tabs. EditorWindow: current_page method is now a read-only property; Marked several functions as private, since they are used nowhere else; Readded short_title method, which calls short_title in the current page; Moved open_module and find_module to editorpage; editorpage: Moved some code at __init__ to a post_init() function to avoid troubles during page creation; PyShell: Some better names, and other misc changes; tabbedpages: Changed absolute imports to relative imports, following the rest of idlelib. Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Fri Aug 1 04:14:29 2008 @@ -1,7 +1,6 @@ import os import re import sys -import imp import traceback import webbrowser import tkMessageBox @@ -28,23 +27,6 @@ # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 -def _find_module(fullname, path=None): - """Version of imp.find_module() that handles hierarchical module names""" - - file = None - for tgt in fullname.split('.'): - if file is not None: - file.close() # close intermediate files - (file, filename, descr) = imp.find_module(tgt, path) - if descr[2] == imp.PY_SOURCE: - break # find but not load the source file - module = imp.load_module(tgt, file, filename, descr) - try: - path = module.__path__ - except AttributeError: - raise ImportError, 'No source for module ' + module.__name__ - return file, filename, descr - class EditorWindow(object): from ColorDelegator import ColorDelegator # overridden by PyShell from UndoDelegator import UndoDelegator # overridden by PyShell @@ -126,18 +108,18 @@ # reside self.text_notebook = TabbedPageSet(top) self.text_notebook.bind('<>', - self.update_controls) + self._update_controls) self.new_tab(filename=filename) - self.text = text = self.current_page().text + self.text = text = self.current_page.text # XXX self.top.focused_widget = self.text self.text_notebook.pack(fill=BOTH, expand=True) - self.io = self.current_page().io + self.io = self.current_page.io # The following "width" attribute is used by PyShell, so keep it here self.width = idleConf.GetOption('main', 'EditorPage', 'width') - self.createmenubar() + self._createmenubar() self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<>", self.close_event) @@ -147,12 +129,12 @@ flist.inversedict[self] = key if key: flist.dict[key] = self - text.bind("<>", self.new_callback) + text.bind("<>", self._new_callback) text.bind("<>", self.flist.close_all_callback) - text.bind("<>", self.open_class_browser) - text.bind("<>", self.open_path_browser) + text.bind("<>", self._open_class_browser) + text.bind("<>", self._open_path_browser) - self.create_statusbar() + self._create_statusbar() top.after_idle(self.set_line_and_column) # usetabs true -> literal tab characters are used by indent and @@ -219,12 +201,20 @@ self.askinteger = tkSimpleDialog.askinteger self.showerror = tkMessageBox.showerror - def new_callback(self, event): - # XXX ?? - current_page = self.current_page() - dirname, basename = current_page.io.defaultfilename() - self.flist.new(dirname) - return "break" + @property + def current_page(self): + """Return the active EditorPage in EditorWindow.""" + curr_tab = self.text_notebook.select() + if TTK: + page = self.text_notebook.pages[self.text_notebook.tab( + curr_tab)['text']].editpage + else: + page = self.text_notebook.pages[curr_tab].editpage + return page + + def short_title(self): + # overriden by PyShell + self.current_page.short_title() def next_tab(self, event): """Show next tab if not in the last tab already.""" @@ -245,14 +235,11 @@ page_title = "#%d" % (len(self.text_notebook.pages) + 1) page = self.text_notebook.add_page(page_title) - if TTK: - parent = page.frame - else: - parent = page.frame - - vbar = Scrollbar(parent, name='vbar') - page.editpage = EditorPage(parent, self, filename=filename, name='text', + vbar = Scrollbar(page.frame, name='vbar') + page.editpage = EditorPage(page.frame, self, name='text', padx=5, wrap='none') + page.editpage.post_init(filename=filename) + text = page.editpage.text vbar['command'] = text.yview @@ -272,43 +259,12 @@ return "break" - def update_controls(self, event): - self.io = self.current_page().io - self.set_line_and_column() - - def create_statusbar(self): - self.status_bar = MultiStatusBar(self.top) - if macosxSupport.runningAsOSXApp(): - # Insert some padding to avoid obscuring some of the statusbar - # by the resize widget. - self.status_bar.set_label('_padding1', ' ', side=RIGHT) - self.status_bar.set_label('column', 'Col: ?', side=RIGHT) - self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) - self.status_bar.pack(side=BOTTOM, fill=X) - def set_line_and_column(self, event=None): # Used by PyShell too - line, column = self.current_page().text.index(INSERT).split('.') + line, column = self.current_page.text.index(INSERT).split('.') self.status_bar.set_label('column', 'Col: %s' % column) self.status_bar.set_label('line', 'Ln: %s' % line) - def createmenubar(self): - mbar = self.menubar - self.menudict = menudict = {} - for name, label in self.menu_specs: - underline, label = prepstr(label) - menudict[name] = menu = Menu(mbar, name=name, tearoff=0) - mbar.add_cascade(label=label, menu=menu, underline=underline) - - if sys.platform == 'darwin' and '.framework' in sys.executable: - # Insert the application menu - menudict['application'] = menu = Menu(mbar, name='apple') - mbar.add_cascade(label='IDLE', menu=menu) - - self.fill_menus() - self.base_helpmenu_length = self.menudict['help'].index(END) - self.reset_help_menu_entries() - def postwindowsmenu(self): # Only called when Windows menu exists menu = self.menudict['windows'] @@ -319,79 +275,20 @@ menu.delete(self.wmenu_end+1, end) WindowList.add_windows_to_menu(menu) - def open_module(self, event=None): # XXX depends on self.text - # XXX Shouldn't this be in IOBinding or in FileList? - try: - name = self.text.get("sel.first", "sel.last") - except TclError: - name = "" - else: - name = name.strip() - name = tkSimpleDialog.askstring("Module", - "Enter the name of a Python module\n" - "to search on sys.path and open:", - parent=self.text, initialvalue=name) - if name: - name = name.strip() - if not name: - return - # XXX Ought to insert current file's directory in front of path - try: - (f, file, (suffix, mode, type)) = _find_module(name) - except (NameError, ImportError), msg: - tkMessageBox.showerror("Import error", str(msg), parent=self.text) - return - if type != imp.PY_SOURCE: - tkMessageBox.showerror("Unsupported type", - "%s is not a source module" % name, parent=self.text) - return - if f: - f.close() - if self.flist: - self.flist.open(file) # XXX change this to create a new tab instead - else: - self.io.loadfile(file) - - def open_class_browser(self, event=None): # XXX depends on self.text - filename = self.io.filename - if not filename: - tkMessageBox.showerror( - "No filename", - "This buffer has no associated filename", - master=self.text) - self.text.focus_set() - return None - head, tail = os.path.split(filename) - base, ext = os.path.splitext(tail) - ClassBrowser.ClassBrowser(self.flist, base, [head]) - - def open_path_browser(self, event=None): - PathBrowser.PathBrowser(self.flist) - - def current_page(self): - """Return the active EditorPage in EditorWindow.""" - curr_tab = self.text_notebook.select() - if TTK: - page = self.text_notebook.pages[self.text_notebook.tab( - curr_tab)['text']].editpage - else: - page = self.text_notebook.pages[curr_tab].editpage - return page - def newline_and_indent_event(self, event): """Call newline_and_indent_event on current EditorPage.""" - self.current_page().newline_and_indent_event(event) + self.current_page.newline_and_indent_event(event) def get_selection_indices(self): """Call get_selection_indices on current EditorPage.""" - return self.current_page().get_selection_indices() + return self.current_page.get_selection_indices() def build_char_in_string_func(self, startindex): """Call build_char_in_string_func on current EditorPage.""" - return self.current_page().build_char_in_string_func(startindex) + return self.current_page.build_char_in_string_func(startindex) def gotoline(self, lineno): - page = self.current_page() + page = self.current_page text = page.text if lineno is not None and lineno > 0: @@ -482,13 +379,6 @@ accel = get_accelerator(keydefs, event) menu.entryconfig(index, accelerator=accel) - def set_notabs_indentwidth(self): - "Update the indentwidth if changed and not using tabs in this window" - # Called from configDialog.py - if not self.usetabs: - self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces', - type='int') - def reset_help_menu_entries(self): "Update the additional help entries on the Help menu" help_list = idleConf.GetAllExtraHelpSourcesList() @@ -506,19 +396,16 @@ # and update the menu dictionary self.menudict['help'] = helpmenu - def __extra_help_callback(self, helpfile): - "Create a callback with the helpfile value frozen at definition time" - def display_extra_help(helpfile=helpfile): - if not helpfile.startswith(('www', 'http')): - url = os.path.normpath(helpfile) - if sys.platform[:3] == 'win': - os.startfile(helpfile) - else: - webbrowser.open(helpfile) - return display_extra_help + def set_notabs_indentwidth(self): + "Update the indentwidth if changed and not using tabs in this window" + # Called from configDialog.py + if not self.usetabs: + self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces', + type='int') def update_recent_files_list(self, new_file=None): "Load and update the recent files list and menus" + # IOBinding calls this rf_list = [] if os.path.exists(self.recent_files_path): rf_list_file = open(self.recent_files_path,'r') @@ -557,13 +444,8 @@ command=callback, underline=0) - def __recent_file_callback(self, file_name): - def open_recent_file(fn_closure=file_name): - self.io.open(editFile=fn_closure) - return open_recent_file - def get_saved(self): - return self.current_page().undo.get_saved() # XXX Pretty wrong + return self.current_page.undo.get_saved() # XXX Pretty wrong def get_geometry(self): "Return (width, height, x, y)" @@ -636,7 +518,7 @@ cls = getattr(mod, name) keydefs = idleConf.GetExtensionBindings(name) if hasattr(cls, "menudefs"): - self.fill_menus(cls.menudefs, keydefs) + self._fill_menus(cls.menudefs, keydefs) ins = cls(self) self.extensions[name] = ins if keydefs: @@ -662,43 +544,6 @@ if keylist: text.event_add(event, *keylist) - def fill_menus(self, menudefs=None, keydefs=None):# XXX depends on self.text - """Add appropriate entries to the menus and submenus - - Menus that are absent or None in self.menudict are ignored. - """ - if menudefs is None: - menudefs = Bindings.menudefs - if keydefs is None: - keydefs = Bindings.default_keydefs - menudict = self.menudict - text = self.text - for mname, entrylist in menudefs: - menu = menudict.get(mname) - if not menu: - continue - for entry in entrylist: - if not entry: - menu.add_separator() - else: - label, eventname = entry - checkbutton = (label[:1] == '!') - if checkbutton: - label = label[1:] - underline, label = prepstr(label) - accelerator = get_accelerator(keydefs, eventname) - def command(text=text, eventname=eventname): - text.event_generate(eventname) - if checkbutton: - var = self.get_var_obj(eventname, BooleanVar) - menu.add_checkbutton(label=label, underline=underline, - command=command, accelerator=accelerator, - variable=var) - else: - menu.add_command(label=label, underline=underline, - command=command, - accelerator=accelerator) - def getvar(self, name): var = self.get_var_obj(name) if var: @@ -767,6 +612,114 @@ indentsmall = indentlarge = 0 return indentlarge - indentsmall + # Private methods + + def _update_controls(self, event): + self.io = self.current_page.io + self.set_line_and_column() + + def _create_statusbar(self): + self.status_bar = MultiStatusBar(self.top) + if macosxSupport.runningAsOSXApp(): + # Insert some padding to avoid obscuring some of the statusbar + # by the resize widget. + self.status_bar.set_label('_padding1', ' ', side=RIGHT) + self.status_bar.set_label('column', 'Col: ?', side=RIGHT) + self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) + self.status_bar.pack(side=BOTTOM, fill=X) + + def _createmenubar(self): + mbar = self.menubar + self.menudict = menudict = {} + for name, label in self.menu_specs: + underline, label = prepstr(label) + menudict[name] = menu = Menu(mbar, name=name, tearoff=0) + mbar.add_cascade(label=label, menu=menu, underline=underline) + + if sys.platform == 'darwin' and '.framework' in sys.executable: + # Insert the application menu + menudict['application'] = menu = Menu(mbar, name='apple') + mbar.add_cascade(label='IDLE', menu=menu) + + self._fill_menus() + self.base_helpmenu_length = self.menudict['help'].index(END) + self.reset_help_menu_entries() + + def _fill_menus(self, menudefs=None, keydefs=None): + # XXX depends on self.text + """Add appropriate entries to the menus and submenus + + Menus that are absent or None in self.menudict are ignored. + """ + if menudefs is None: + menudefs = Bindings.menudefs + if keydefs is None: + keydefs = Bindings.default_keydefs + menudict = self.menudict + text = self.text + for mname, entrylist in menudefs: + menu = menudict.get(mname) + if not menu: + continue + for entry in entrylist: + if not entry: + menu.add_separator() + else: + label, eventname = entry + checkbutton = (label[:1] == '!') + if checkbutton: + label = label[1:] + underline, label = prepstr(label) + accelerator = get_accelerator(keydefs, eventname) + def command(text=text, eventname=eventname): + text.event_generate(eventname) + if checkbutton: + var = self.get_var_obj(eventname, BooleanVar) + menu.add_checkbutton(label=label, underline=underline, + command=command, accelerator=accelerator, + variable=var) + else: + menu.add_command(label=label, underline=underline, + command=command, + accelerator=accelerator) + + def _new_callback(self, event): + dirname, basename = self.current_page.io.defaultfilename() + self.flist.new(dirname) + return "break" + + def _open_class_browser(self, event=None): + filename = self.io.filename + if not filename: + tkMessageBox.showerror( + "No filename", + "This buffer has no associated filename", + master=self.text_notebook) + self.current_page.text.focus_set() + return None + head, tail = os.path.split(filename) + base, ext = os.path.splitext(tail) + ClassBrowser.ClassBrowser(self.flist, base, [head]) + + def _open_path_browser(self, event=None): + PathBrowser.PathBrowser(self.flist) + + def __recent_file_callback(self, file_name): + def open_recent_file(fn_closure=file_name): + self.io.open(editFile=fn_closure) + return open_recent_file + + def __extra_help_callback(self, helpfile): + "Create a callback with the helpfile value frozen at definition time" + def display_extra_help(helpfile=helpfile): + if not helpfile.startswith(('www', 'http')): + url = os.path.normpath(helpfile) + if sys.platform[:3] == 'win': + os.startfile(helpfile) + else: + webbrowser.open(helpfile) + return display_extra_help + # Look at the leading whitespace in s. # Return pair (# of leading ws characters, # effective # of leading blanks after expanding Modified: sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py Fri Aug 1 04:14:29 2008 @@ -1,6 +1,15 @@ import os import tkMessageBox +def _canonize(filename): + if not os.path.isabs(filename): + try: + pwd = os.getcwd() + except os.error: + pass + else: + filename = os.path.join(pwd, filename) + return os.path.normpath(filename) class FileList: @@ -15,7 +24,7 @@ def open(self, filename, action=None): assert filename - filename = self.canonize(filename) + filename = _canonize(filename) if os.path.isdir(filename): # This can happen when bad filename is passed on command line: tkMessageBox.showerror( @@ -61,20 +70,20 @@ if not self.inversedict: self.root.quit() - def filename_changed_edit(self, edit): - edit.saved_change_hook() + def filename_changed_edit(self, page, editwin): + page.saved_change_hook() try: - key = self.inversedict[edit] + key = self.inversedict[editwin] except KeyError: print "Don't know this EditorWindow object. (rename)" return - filename = edit.io.filename + filename = page.io.filename if not filename: if key: del self.dict[key] self.inversedict[edit] = None return - filename = self.canonize(filename) + filename = _canonize(filename) newkey = os.path.normcase(filename) if newkey == key: return @@ -93,16 +102,6 @@ except KeyError: pass - def canonize(self, filename): - if not os.path.isabs(filename): - try: - pwd = os.getcwd() - except os.error: - pass - else: - filename = os.path.join(pwd, filename) - return os.path.normpath(filename) - def _test(): import sys Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Fri Aug 1 04:14:29 2008 @@ -122,11 +122,10 @@ #self.breakpoints = [] EditorWindow.__init__(self, *args) - self.top.bind('<>', self._configure_new_tab) - self._configure_new_tab() - self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(), 'breakpoints.lst') + self.top.bind('<>', self._configure_new_tab) + self._configure_new_tab() def _configure_new_tab(self, event=None): page = self.text_notebook.last_page().editpage @@ -140,7 +139,9 @@ text.bind("<>", self.flist.open_shell) # whenever a file is changed, restore breakpoints - if page.io.filename: self.restore_file_breaks(text, page) + if page.io.filename: + self.restore_file_breaks(text, page) + def filename_changed_hook(old_hook=page.io.filename_change_hook, self=self): self.restore_file_breaks(text, page) @@ -1228,7 +1229,7 @@ s = "" self.console.write(s) - curr_page = self.current_page() + curr_page = self.current_page curr_page.text.mark_set("insert", "end-1c") self.set_line_and_column() curr_page.io.reset_undo() @@ -1412,13 +1413,13 @@ # create base styles used along idle files style = Style() - x = style.map('.') - r = {'background': []} - for sspec in x.get('background', []): + rootbg = style.map('.', 'background') + fstyle = {'background': []} + for sspec in rootbg: if 'active' in sspec[:-1]: - r['background'].append(('!disabled', sspec[-1])) + fstyle['background'].append(('!disabled', sspec[-1])) break - style.map('RootColor.TFrame', **r) + style.map('RootColor.TFrame', **fstyle) # end styles fixwordbreaks(root) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Fri Aug 1 04:14:29 2008 @@ -1,6 +1,8 @@ import os import sys +import imp import webbrowser +import tkMessageBox import tkSimpleDialog from Tkinter import Text, Menu, TclError @@ -49,8 +51,26 @@ # byte-to-byte conversion return filename.decode('iso8859-1') -class EditorPage: - def __init__(self, parent_frame, editwin, filename=None, **kwargs): +def _find_module(fullname, path=None): + """Version of imp.find_module() that handles hierarchical module names""" + file = None + + for tgt in fullname.split('.'): + if file is not None: + file.close() # close intermediate files + (file, filename, descr) = imp.find_module(tgt, path) + if descr[2] == imp.PY_SOURCE: + break # find but not load the source file + module = imp.load_module(tgt, file, filename, descr) + try: + path = module.__path__ + except AttributeError: + raise ImportError('No source for module %s' % module.__name__) + + return file, filename, descr + +class EditorPage(object): + def __init__(self, parent_frame, editwin, **kwargs): self.editwin = editwin kwargs.setdefault('width', idleConf.GetOption('main', 'EditorPage', 'width')) @@ -58,18 +78,20 @@ 'height')) self.text = MultiCallCreator(Text)(parent_frame, **kwargs) - self.color = None # initialized in reset_colorizer - self.per = Percolator(self.text) self.undo = self.editwin.UndoDelegator() self.per.insertfilter(self.undo) self.text.undo_block_start = self.undo.undo_block_start self.text.undo_block_stop = self.undo.undo_block_stop - self.undo.set_saved_change_hook(self.saved_change_hook) - self.io = IOBinding.IOBinding(self) + + self.undo.set_saved_change_hook(self.saved_change_hook) self.io.set_filename_change_hook(self.filename_change_hook) + self.reset_colorizer() + self._setup_bindings() + + def post_init(self, filename=None): if filename: if os.path.exists(filename) and not os.path.isdir(filename): self.io.loadfile(filename) @@ -77,10 +99,6 @@ self.io.set_filename(filename) self.saved_change_hook() - self.reset_colorizer() - - self._setup_bindings() - def close(self): """Perform necessary cleanup for this page before closing it.""" self.io.close() @@ -97,7 +115,7 @@ # XXX (1) mark where these functions are used def saved_change_hook(self): - short = self.short_title() + short = self.editwin.short_title() long = self.long_title() if short and long: @@ -123,10 +141,15 @@ self.undo.set_saved(flag) def filename_change_hook(self): + try: + print self, self.editwin, self.editwin.inversedict + except AttributeError: # PyShell + pass + if self.editwin.flist: - self.editwin.flist.filename_changed_edit(self) + self.editwin.flist.filename_changed_edit(self, self.editwin) self.saved_change_hook() - self.editwin.top.update_windowlist_registry(self) + self.editwin.top.update_windowlist_registry(self.editwin) self.reset_colorizer() def reset_undo(self): @@ -206,6 +229,7 @@ # adjust indentation for continuations and block # open/close first need to find the last stmt lno = index2line(text.index('insert')) + #print self.editwin.indentwidth, self.editwin.tabwidth y = PyParse.Parser(self.editwin.indentwidth, self.editwin.tabwidth) if not self.editwin.context_use_ps1: for context in self.editwin.num_context_lines: @@ -300,7 +324,7 @@ def _setup_bindings(self): text = self.text actions = ('<>', '<>', '<>', - '<>', '<>', + '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>') @@ -322,8 +346,7 @@ method_name = event[prefix_size:-prefix_size].replace('-', '_') text.bind(event, getattr(self, "_%s_event" % method_name.lower())) - parent_actions = ('<>', '<>', '<>', - '<>') + parent_actions = ('<>', '<>', '<>') for action in parent_actions: prefix_size = action.count('<') @@ -368,6 +391,42 @@ # parent configDialog.ConfigDialog(self.editwin.top, 'Settings') + def _open_module(self, event=None): + try: + name = self.text.get("sel.first", "sel.last") + except TclError: + name = "" + else: + name = name.strip() + + name = tkSimpleDialog.askstring("Module", + "Enter the name of a Python module\n" + "to search on sys.path and open:", + parent=self.text, initialvalue=name) + + if name: + name = name.strip() + if not name: + return + # XXX Ought to insert current file's directory in front of path + try: + (f, file, (suffix, mode, type)) = _find_module(name) + except (NameError, ImportError), msg: + tkMessageBox.showerror("Import error", str(msg), parent=self.text) + return + + if type != imp.PY_SOURCE: + tkMessageBox.showerror("Unsupported type", + "%s is not a source module" % name, parent=self.text) + return + if f: + f.close() + if self.editwin.flist: # XXX + # XXX change this to create a new tab instead + self.editwin.flist.open(file) + else: + self.io.loadfile(file) + def _find_event(self, event): SearchDialog.find(self.text) return "break" Modified: sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages.py Fri Aug 1 04:14:29 2008 @@ -5,8 +5,8 @@ def get_tabbedpage(): """Returns the TabbedPageSet available for use.""" try: - from idlelib.tabbedpages_new import TabbedPageSet + from tabbedpages_new import TabbedPageSet except ImportError: - from idlelib.tabbedpages_old import TabbedPageSet + from tabbedpages_old import TabbedPageSet return TabbedPageSet From python-checkins at python.org Fri Aug 1 04:22:01 2008 From: python-checkins at python.org (guilherme.polo) Date: Fri, 1 Aug 2008 04:22:01 +0200 (CEST) Subject: [Python-checkins] r65357 - sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Message-ID: <20080801022201.2262D1E4005@bag.python.org> Author: guilherme.polo Date: Fri Aug 1 04:22:00 2008 New Revision: 65357 Log: Fixed IndentationError, oops Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Fri Aug 1 04:22:00 2008 @@ -391,7 +391,7 @@ # parent configDialog.ConfigDialog(self.editwin.top, 'Settings') - def _open_module(self, event=None): + def _open_module(self, event=None): try: name = self.text.get("sel.first", "sel.last") except TclError: From python-checkins at python.org Fri Aug 1 06:55:26 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 06:55:26 +0200 (CEST) Subject: [Python-checkins] r65358 - tracker/instances/python-dev/html/_generic.404.html Message-ID: <20080801045526.8C55C1E4015@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 06:55:26 2008 New Revision: 65358 Log: Add 404 template from classic scheme. Added: tracker/instances/python-dev/html/_generic.404.html (contents, props changed) Added: tracker/instances/python-dev/html/_generic.404.html ============================================================================== --- (empty file) +++ tracker/instances/python-dev/html/_generic.404.html Fri Aug 1 06:55:26 2008 @@ -0,0 +1,9 @@ + + +Item Not Found + + + +There is no with id + + From python-checkins at python.org Fri Aug 1 07:10:47 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 07:10:47 +0200 (CEST) Subject: [Python-checkins] r65359 - tracker/roundup-src/roundup/cgi/actions.py Message-ID: <20080801051047.25AEF1E4005@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 07:10:46 2008 New Revision: 65359 Log: Validate CSV export request. Modified: tracker/roundup-src/roundup/cgi/actions.py Modified: tracker/roundup-src/roundup/cgi/actions.py ============================================================================== --- tracker/roundup-src/roundup/cgi/actions.py (original) +++ tracker/roundup-src/roundup/cgi/actions.py Fri Aug 1 07:10:46 2008 @@ -974,6 +974,14 @@ columns = request.columns klass = self.db.getclass(request.classname) + # validate the request + allprops = klass.getprops() + for c in filterspec.keys() + columns + [x[1] for x in group + sort]: + if not allprops.has_key(c): + # Can't use FormError, since that would try to use + # the same bogus field specs + raise exceptions.SeriousError, "Property %s does not exist" % c + # full-text search if request.search_text: matches = self.db.indexer.search( From python-checkins at python.org Fri Aug 1 07:16:00 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 07:16:00 +0200 (CEST) Subject: [Python-checkins] r65360 - tracker/instances/board/html/_generic.404.html Message-ID: <20080801051600.4C7171E400C@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 07:16:00 2008 New Revision: 65360 Log: Add 404 template Added: tracker/instances/board/html/_generic.404.html - copied unchanged from r65359, /tracker/instances/python-dev/html/_generic.404.html From python-checkins at python.org Fri Aug 1 07:17:10 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 07:17:10 +0200 (CEST) Subject: [Python-checkins] r65361 - tracker/instances/jobs/html/_generic.404.html Message-ID: <20080801051710.BE74E1E4011@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 07:17:10 2008 New Revision: 65361 Log: Add 404 template Added: tracker/instances/jobs/html/_generic.404.html - copied unchanged from r65360, /tracker/instances/python-dev/html/_generic.404.html From python-checkins at python.org Fri Aug 1 07:17:22 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 07:17:22 +0200 (CEST) Subject: [Python-checkins] r65362 - tracker/instances/jython/html/_generic.404.html Message-ID: <20080801051722.A48BE1E401A@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 07:17:22 2008 New Revision: 65362 Log: Add 404 template Added: tracker/instances/jython/html/_generic.404.html - copied unchanged from r65361, /tracker/instances/python-dev/html/_generic.404.html From python-checkins at python.org Fri Aug 1 07:17:36 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 07:17:36 +0200 (CEST) Subject: [Python-checkins] r65363 - tracker/instances/meta/html/_generic.404.html Message-ID: <20080801051736.2B8721E4005@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 07:17:35 2008 New Revision: 65363 Log: Add 404 template Added: tracker/instances/meta/html/_generic.404.html - copied unchanged from r65362, /tracker/instances/python-dev/html/_generic.404.html From python-checkins at python.org Fri Aug 1 07:17:47 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 07:17:47 +0200 (CEST) Subject: [Python-checkins] r65364 - tracker/instances/security/html/_generic.404.html Message-ID: <20080801051747.E01C91E4005@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 07:17:47 2008 New Revision: 65364 Log: Add 404 template Added: tracker/instances/security/html/_generic.404.html - copied unchanged from r65363, /tracker/instances/python-dev/html/_generic.404.html From buildbot at python.org Fri Aug 1 07:19:49 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 05:19:49 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 trunk Message-ID: <20080801051949.4F1E91E4005@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%20trunk/builds/3763 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 07:45:57 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 05:45:57 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian trunk Message-ID: <20080801054557.699811E4005@bag.python.org> The Buildbot has detected a new failure of S-390 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%20trunk/builds/925 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 10:33:29 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 08:33:29 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 3.0 Message-ID: <20080801083329.4C76A1E4002@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%203.0/builds/80 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: mark.dickinson BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From python-checkins at python.org Fri Aug 1 11:13:07 2008 From: python-checkins at python.org (mark.dickinson) Date: Fri, 1 Aug 2008 11:13:07 +0200 (CEST) Subject: [Python-checkins] r65366 - python/trunk/Doc/library/math.rst Message-ID: <20080801091307.72DFB1E4006@bag.python.org> Author: mark.dickinson Date: Fri Aug 1 11:13:07 2008 New Revision: 65366 Log: Tone down math.fsum warning. Modified: python/trunk/Doc/library/math.rst Modified: python/trunk/Doc/library/math.rst ============================================================================== --- python/trunk/Doc/library/math.rst (original) +++ python/trunk/Doc/library/math.rst Fri Aug 1 11:13:07 2008 @@ -92,29 +92,8 @@ .. note:: - On platforms where arithmetic results are not correctly rounded, - :func:`fsum` may occasionally produce incorrect results; these - results should be no less accurate than those from the builtin - :func:`sum` function, but nevertheless may have arbitrarily - large relative error. - - In particular, this affects some older Intel hardware (for - example Pentium and earlier x86 processors) that makes use of - 'extended precision' floating-point registers with 64 bits of - precision instead of the 53 bits of precision provided by a C - double. Arithmetic operations using these registers may be - doubly rounded (rounded first to 64 bits, and then rerounded to - 53 bits), leading to incorrectly rounded results. To test - whether your machine is one of those affected, try the following - at a Python prompt:: - - >>> 1e16 + 2.9999 - 10000000000000002.0 - - Machines subject to the double-rounding problem described above - are likely to print ``10000000000000004.0`` instead of - ``10000000000000002.0``. - + The accuracy of fsum() may be impaired on builds that use + extended precision addition and then double-round the results. .. versionadded:: 2.6 From buildbot at python.org Fri Aug 1 11:34:49 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 09:34:49 +0000 Subject: [Python-checkins] buildbot failure in OS X x86 3.0 Message-ID: <20080801093449.83DD61E4006@bag.python.org> The Buildbot has detected a new failure of OS X x86 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/OS%20X%20x86%203.0/builds/120 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: noller-osx86 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: mark.dickinson BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Abort trap sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 12:20:03 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 10:20:03 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080801102003.8F85B1E4006@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1179 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: mark.dickinson BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_xmlrpc_net make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 13:13:00 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 11:13:00 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080801111300.BA4ED1E4015@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/84 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: mark.dickinson BUILD FAILED: failed compile sincerely, -The Buildbot From python-checkins at python.org Fri Aug 1 16:10:27 2008 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 1 Aug 2008 16:10:27 +0200 (CEST) Subject: [Python-checkins] r65368 - in python/trunk: Makefile.pre.in Misc/NEWS Message-ID: <20080801141027.5770A1E4006@bag.python.org> Author: martin.v.loewis Date: Fri Aug 1 16:10:26 2008 New Revision: 65368 Log: Generate the PatternGrammar pickle during "make install". Fixes part of #3131. Modified: python/trunk/Makefile.pre.in python/trunk/Misc/NEWS Modified: python/trunk/Makefile.pre.in ============================================================================== --- python/trunk/Makefile.pre.in (original) +++ python/trunk/Makefile.pre.in Fri Aug 1 16:10:26 2008 @@ -895,7 +895,7 @@ -d $(LIBDEST)/site-packages -f \ -x badsyntax $(DESTDIR)$(LIBDEST)/site-packages -PYTHONPATH=$(DESTDIR)$(LIBDEST) $(RUNSHARED) \ - ./$(BUILDPYTHON) -Wi -t -c "import lib2to3.pygram" + ./$(BUILDPYTHON) -Wi -t -c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()" # Create the PLATDIR source directory, if one wasn't distributed.. $(srcdir)/Lib/$(PLATDIR): Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Fri Aug 1 16:10:26 2008 @@ -68,6 +68,11 @@ file name rather than a ZipInfo instance, so files are extracted with mode 0600 rather than 000 under Unix. +Build +----- + +- Generate the PatternGrammar pickle during "make install". + What's New in Python 2.6 beta 2? ================================ From buildbot at python.org Fri Aug 1 16:19:51 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 14:19:51 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP trunk Message-ID: <20080801141951.8C18E1E4006@bag.python.org> The Buildbot has detected a new failure of amd64 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%20trunk/builds/104 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: mark.dickinson,martin.v.loewis BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 16:21:42 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 14:21:42 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080801142142.C9A8A1E4006@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/86 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: martin.v.loewis BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 16:28:20 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 14:28:20 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo trunk Message-ID: <20080801142820.393441E4006@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/1253 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: mark.dickinson,martin.v.loewis BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 16:44:49 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 14:44:49 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo trunk Message-ID: <20080801144449.C77B21E4006@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%20trunk/builds/4011 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-x86 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: mark.dickinson,martin.v.loewis BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_pickletools make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 18:41:41 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 16:41:41 +0000 Subject: [Python-checkins] buildbot failure in OS X x86 3.0 Message-ID: <20080801164141.7C71A1E4007@bag.python.org> The Buildbot has detected a new failure of OS X x86 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/OS%20X%20x86%203.0/builds/122 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: noller-osx86 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: benjamin.peterson BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_multiprocessing make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Fri Aug 1 18:45:04 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 16:45:04 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080801164504.229731E4006@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/866 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: benjamin.peterson BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_smtplib ====================================================================== FAIL: testSend (test.test_smtplib.DebuggingServerTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_smtplib.py", line 229, in testSend self.assertEqual(self.output.getvalue(), mexpect) AssertionError: "---------- MESSAGE FOLLOWS ----------\nA test message\n------------ END MESSAGE ------------\nerror: uncaptured python exception, closing channel (:[Errno 9] Bad file descriptor [/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/asyncore.py|readwrite|100] [/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/asyncore.py|handle_write_event|426])\n" != '---------- MESSAGE FOLLOWS ----------\nA test message\n------------ END MESSAGE ------------\n' make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Fri Aug 1 21:11:23 2008 From: python-checkins at python.org (georg.brandl) Date: Fri, 1 Aug 2008 21:11:23 +0200 (CEST) Subject: [Python-checkins] r65372 - in doctools/branches/0.4.x: EXAMPLES Makefile sphinx/builder.py sphinx/environment.py tests/etree13 tests/etree13/ElementPath.py tests/etree13/ElementTree.py tests/etree13/HTMLTreeBuilder.py tests/etree13/__init__.py tests/root/conf.py tests/root/contents.txt tests/root/images.txt tests/root/img.gif tests/root/img.pdf tests/root/img.png tests/root/subdir tests/root/subdir/img.png tests/test_build.py tests/test_env.py tests/test_markup.py tests/util.py Message-ID: <20080801191123.D7EE61E4012@bag.python.org> Author: georg.brandl Date: Fri Aug 1 21:11:22 2008 New Revision: 65372 Log: Add more tests, fix a few bugs in image handling. Added: doctools/branches/0.4.x/tests/etree13/ doctools/branches/0.4.x/tests/etree13/ElementPath.py (contents, props changed) doctools/branches/0.4.x/tests/etree13/ElementTree.py (contents, props changed) doctools/branches/0.4.x/tests/etree13/HTMLTreeBuilder.py (contents, props changed) doctools/branches/0.4.x/tests/etree13/__init__.py (contents, props changed) doctools/branches/0.4.x/tests/root/images.txt (contents, props changed) doctools/branches/0.4.x/tests/root/img.gif (contents, props changed) doctools/branches/0.4.x/tests/root/img.pdf (contents, props changed) doctools/branches/0.4.x/tests/root/img.png (contents, props changed) doctools/branches/0.4.x/tests/root/subdir/ doctools/branches/0.4.x/tests/root/subdir/img.png (contents, props changed) doctools/branches/0.4.x/tests/test_build.py (contents, props changed) doctools/branches/0.4.x/tests/test_env.py (contents, props changed) Modified: doctools/branches/0.4.x/EXAMPLES doctools/branches/0.4.x/Makefile doctools/branches/0.4.x/sphinx/builder.py doctools/branches/0.4.x/sphinx/environment.py doctools/branches/0.4.x/tests/root/conf.py doctools/branches/0.4.x/tests/root/contents.txt doctools/branches/0.4.x/tests/test_markup.py doctools/branches/0.4.x/tests/util.py Modified: doctools/branches/0.4.x/EXAMPLES ============================================================================== --- doctools/branches/0.4.x/EXAMPLES (original) +++ doctools/branches/0.4.x/EXAMPLES Fri Aug 1 21:11:22 2008 @@ -19,6 +19,7 @@ * PyUblas: http://tiker.net/doc/pyublas/ * Py on Windows: http://timgolden.me.uk/python-on-windows/ * Zope 3: e.g. http://docs.carduner.net/z3c-tutorial/ +* Glashammer: http://glashammer.welterde.de/ * SymPy: http://docs.sympy.org/ * Grok (upcoming) * Django (upcoming) Modified: doctools/branches/0.4.x/Makefile ============================================================================== --- doctools/branches/0.4.x/Makefile (original) +++ doctools/branches/0.4.x/Makefile Fri Aug 1 21:11:22 2008 @@ -27,4 +27,4 @@ @$(PYTHON) utils/reindent.py -r -B . test: - @cd tests; $(PYTHON) run.py -d + @cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST) Modified: doctools/branches/0.4.x/sphinx/builder.py ============================================================================== --- doctools/branches/0.4.x/sphinx/builder.py (original) +++ doctools/branches/0.4.x/sphinx/builder.py Fri Aug 1 21:11:22 2008 @@ -143,6 +143,9 @@ node['uri'] = candidate else: candidate = node['uri'] + if candidate not in self.env.images: + # non-existing URI; let it alone + continue self.images[candidate] = self.env.images[candidate][1] # build methods Modified: doctools/branches/0.4.x/sphinx/environment.py ============================================================================== --- doctools/branches/0.4.x/sphinx/environment.py (original) +++ doctools/branches/0.4.x/sphinx/environment.py Fri Aug 1 21:11:22 2008 @@ -556,12 +556,12 @@ imgpath = path.normpath(path.join(docdir, imguri)) node['uri'] = imgpath if imgpath.endswith(os.extsep + '*'): - for filename in glob(imgpath): - basename, ext = os.path.splitext(filename) - if ext == '.pdf': - candidates['application/pdf'] = filename - elif ext == '.svg': - candidates['image/svg+xml'] = filename + for filename in glob(path.join(self.srcdir, imgpath)): + dir, base = path.split(filename) + if base.lower().endswith('.pdf'): + candidates['application/pdf'] = path.join(docdir, base) + elif base.lower().endswith('.svg'): + candidates['image/svg+xml'] = path.join(docdir, base) else: f = open(filename, 'rb') try: @@ -569,23 +569,23 @@ finally: f.close() if imgtype: - candidates['image/' + imgtype] = filename + candidates['image/' + imgtype] = path.join(docdir, base) else: candidates['*'] = imgpath - for img in candidates.itervalues(): - self.dependencies.setdefault(docname, set()).add(img) - if not os.access(path.join(self.srcdir, img), os.R_OK): - self.warn(docname, 'Image file not readable: %s' % img, node.line) - if img in self.images: - self.images[img][0].add(docname) + for imgpath in candidates.itervalues(): + self.dependencies.setdefault(docname, set()).add(imgpath) + if not os.access(path.join(self.srcdir, imgpath), os.R_OK): + self.warn(docname, 'Image file not readable: %s' % imgpath, node.line) + if imgpath in self.images: + self.images[imgpath][0].add(docname) continue - uniquename = path.basename(img) + uniquename = path.basename(imgpath) base, ext = path.splitext(uniquename) i = 0 while uniquename in existing_names: i += 1 uniquename = '%s%s%s' % (base, i, ext) - self.images[img] = (set([docname]), uniquename) + self.images[imgpath] = (set([docname]), uniquename) existing_names.add(uniquename) def process_metadata(self, docname, doctree): Added: doctools/branches/0.4.x/tests/etree13/ElementPath.py ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/etree13/ElementPath.py Fri Aug 1 21:11:22 2008 @@ -0,0 +1,226 @@ +# +# ElementTree +# $Id$ +# +# limited xpath support for element trees +# +# history: +# 2003-05-23 fl created +# 2003-05-28 fl added support for // etc +# 2003-08-27 fl fixed parsing of periods in element names +# 2007-09-10 fl new selection engine +# +# Copyright (c) 2003-2007 by Fredrik Lundh. All rights reserved. +# +# fredrik at pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Implementation module for XPath support. There's usually no reason +# to import this module directly; the ElementTree does this for +# you, if needed. +## + +import re + +xpath_tokenizer = re.compile( + "(" + "'[^']*'|\"[^\"]*\"|" + "::|" + "//?|" + "\.\.|" + "\(\)|" + "[/.*:\[\]\(\)@=])|" + "((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|" + "\s+" + ).findall + +def prepare_tag(next, token): + tag = token[1] + def select(context, result): + for elem in result: + for e in elem: + if e.tag == tag: + yield e + return select + +def prepare_star(next, token): + def select(context, result): + for elem in result: + for e in elem: + yield e + return select + +def prepare_dot(next, token): + def select(context, result): + for elem in result: + yield elem + return select + +def prepare_iter(next, token): + token = next() + if token[0] == "*": + tag = "*" + elif not token[0]: + tag = token[1] + else: + raise SyntaxError + def select(context, result): + for elem in result: + for e in elem.iter(tag): + if e is not elem: + yield e + return select + +def prepare_dot_dot(next, token): + def select(context, result): + parent_map = context.parent_map + if parent_map is None: + context.parent_map = parent_map = {} + for p in context.root.iter(): + for e in p: + parent_map[e] = p + for elem in result: + if elem in parent_map: + yield parent_map[elem] + return select + +def prepare_predicate(next, token): + # this one should probably be refactored... + token = next() + if token[0] == "@": + # attribute + token = next() + if token[0]: + raise SyntaxError("invalid attribute predicate") + key = token[1] + token = next() + if token[0] == "]": + def select(context, result): + for elem in result: + if elem.get(key) is not None: + yield elem + elif token[0] == "=": + value = next()[0] + if value[:1] == "'" or value[:1] == '"': + value = value[1:-1] + else: + raise SyntaxError("invalid comparision target") + token = next() + def select(context, result): + for elem in result: + if elem.get(key) == value: + yield elem + if token[0] != "]": + raise SyntaxError("invalid attribute predicate") + elif not token[0]: + tag = token[1] + token = next() + if token[0] != "]": + raise SyntaxError("invalid node predicate") + def select(context, result): + for elem in result: + if elem.find(tag) is not None: + yield elem + else: + raise SyntaxError("invalid predicate") + return select + +ops = { + "": prepare_tag, + "*": prepare_star, + ".": prepare_dot, + "..": prepare_dot_dot, + "//": prepare_iter, + "[": prepare_predicate, + } + +_cache = {} + +class _SelectorContext: + parent_map = None + def __init__(self, root): + self.root = root + +# -------------------------------------------------------------------- + +## +# Find first matching object. + +def find(elem, path): + try: + return findall(elem, path).next() + except StopIteration: + return None + +## +# Find all matching objects. + +def findall(elem, path): + # compile selector pattern + try: + selector = _cache[path] + except KeyError: + if len(_cache) > 100: + _cache.clear() + if path[:1] == "/": + raise SyntaxError("cannot use absolute path on element") + stream = iter(xpath_tokenizer(path)) + next = stream.next; token = next() + selector = [] + while 1: + try: + selector.append(ops[token[0]](next, token)) + except StopIteration: + raise SyntaxError("invalid path") + try: + token = next() + if token[0] == "/": + token = next() + except StopIteration: + break + _cache[path] = selector + # execute selector pattern + result = [elem] + context = _SelectorContext(elem) + for select in selector: + result = select(context, result) + return result + +## +# Find text for first matching object. + +def findtext(elem, path, default=None): + try: + elem = findall(elem, path).next() + return elem.text + except StopIteration: + return default Added: doctools/branches/0.4.x/tests/etree13/ElementTree.py ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/etree13/ElementTree.py Fri Aug 1 21:11:22 2008 @@ -0,0 +1,1542 @@ +# +# ElementTree +# $Id$ +# +# light-weight XML support for Python 2.2 and later. +# +# history: +# 2001-10-20 fl created (from various sources) +# 2001-11-01 fl return root from parse method +# 2002-02-16 fl sort attributes in lexical order +# 2002-04-06 fl TreeBuilder refactoring, added PythonDoc markup +# 2002-05-01 fl finished TreeBuilder refactoring +# 2002-07-14 fl added basic namespace support to ElementTree.write +# 2002-07-25 fl added QName attribute support +# 2002-10-20 fl fixed encoding in write +# 2002-11-24 fl changed default encoding to ascii; fixed attribute encoding +# 2002-11-27 fl accept file objects or file names for parse/write +# 2002-12-04 fl moved XMLTreeBuilder back to this module +# 2003-01-11 fl fixed entity encoding glitch for us-ascii +# 2003-02-13 fl added XML literal factory +# 2003-02-21 fl added ProcessingInstruction/PI factory +# 2003-05-11 fl added tostring/fromstring helpers +# 2003-05-26 fl added ElementPath support +# 2003-07-05 fl added makeelement factory method +# 2003-07-28 fl added more well-known namespace prefixes +# 2003-08-15 fl fixed typo in ElementTree.findtext (Thomas Dartsch) +# 2003-09-04 fl fall back on emulator if ElementPath is not installed +# 2003-10-31 fl markup updates +# 2003-11-15 fl fixed nested namespace bug +# 2004-03-28 fl added XMLID helper +# 2004-06-02 fl added default support to findtext +# 2004-06-08 fl fixed encoding of non-ascii element/attribute names +# 2004-08-23 fl take advantage of post-2.1 expat features +# 2004-09-03 fl made Element class visible; removed factory +# 2005-02-01 fl added iterparse implementation +# 2005-03-02 fl fixed iterparse support for pre-2.2 versions +# 2005-11-12 fl added tostringlist/fromstringlist helpers +# 2006-07-05 fl merged in selected changes from the 1.3 sandbox +# 2006-07-05 fl removed support for 2.1 and earlier +# 2007-06-21 fl added deprecation/future warnings +# 2007-08-25 fl added doctype hook, added parser version attribute etc +# 2007-08-26 fl added new serializer code (better namespace handling, etc) +# 2007-08-27 fl warn for broken /tag searches on tree level +# 2007-09-02 fl added html/text methods to serializer (experimental) +# 2007-09-05 fl added method argument to tostring/tostringlist +# 2007-09-06 fl improved error handling +# +# Copyright (c) 1999-2007 by Fredrik Lundh. All rights reserved. +# +# fredrik at pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +from __future__ import generators + +__all__ = [ + # public symbols + "Comment", + "dump", + "Element", "ElementTree", + "fromstring", "fromstringlist", + "iselement", "iterparse", + "parse", "ParseError", + "PI", "ProcessingInstruction", + "QName", + "SubElement", + "tostring", "tostringlist", + "TreeBuilder", + "VERSION", + "XML", + "XMLParser", "XMLTreeBuilder", + ] + +## +# The Element type is a flexible container object, designed to +# store hierarchical data structures in memory. The type can be +# described as a cross between a list and a dictionary. +#

+# Each element has a number of properties associated with it: +#

    +#
  • a tag. This is a string identifying what kind of data +# this element represents (the element type, in other words).
  • +#
  • a number of attributes, stored in a Python dictionary.
  • +#
  • a text string.
  • +#
  • an optional tail string.
  • +#
  • a number of child elements, stored in a Python sequence
  • +#
+# +# To create an element instance, use the {@link #Element} constructor +# or the {@link #SubElement} factory function. +#

+# The {@link #ElementTree} class can be used to wrap an element +# structure, and convert it from and to XML. +## + +import sys, re + +class _SimpleElementPath(object): + # emulate pre-1.2 find/findtext/findall behaviour + def find(self, element, tag): + for elem in element: + if elem.tag == tag: + return elem + return None + def findtext(self, element, tag, default=None): + for elem in element: + if elem.tag == tag: + return elem.text or "" + return default + def findall(self, element, tag): + if tag[:3] == ".//": + return element.getiterator(tag[3:]) + result = [] + for elem in element: + if elem.tag == tag: + result.append(elem) + return result + +try: + import ElementPath +except ImportError: + # FIXME: issue warning in this case? + ElementPath = _SimpleElementPath() + +VERSION = "1.3a2" + +class ParseError(SyntaxError): + pass + +# -------------------------------------------------------------------- + +## +# Checks if an object appears to be a valid element object. +# +# @param An element instance. +# @return A true value if this is an element object. +# @defreturn flag + +def iselement(element): + # FIXME: not sure about this; might be a better idea to look + # for tag/attrib/text attributes + return isinstance(element, Element) or hasattr(element, "tag") + +## +# Element class. This class defines the Element interface, and +# provides a reference implementation of this interface. +#

+# The element name, attribute names, and attribute values can be +# either 8-bit ASCII strings or Unicode strings. +# +# @param tag The element name. +# @param attrib An optional dictionary, containing element attributes. +# @param **extra Additional attributes, given as keyword arguments. +# @see Element +# @see SubElement +# @see Comment +# @see ProcessingInstruction + +class Element(object): + # text...tail + + ## + # (Attribute) Element tag. + + tag = None + + ## + # (Attribute) Element attribute dictionary. Where possible, use + # {@link #Element.get}, + # {@link #Element.set}, + # {@link #Element.keys}, and + # {@link #Element.items} to access + # element attributes. + + attrib = None + + ## + # (Attribute) Text before first subelement. This is either a + # string or the value None, if there was no text. + + text = None + + ## + # (Attribute) Text after this element's end tag, but before the + # next sibling element's start tag. This is either a string or + # the value None, if there was no text. + + tail = None # text after end tag, if any + + def __init__(self, tag, attrib={}, **extra): + attrib = attrib.copy() + attrib.update(extra) + self.tag = tag + self.attrib = attrib + self._children = [] + + def __repr__(self): + return "" % (repr(self.tag), id(self)) + + ## + # Creates a new element object of the same type as this element. + # + # @param tag Element tag. + # @param attrib Element attributes, given as a dictionary. + # @return A new element instance. + + def makeelement(self, tag, attrib): + return Element(tag, attrib) + + ## + # Returns the number of subelements. + # + # @return The number of subelements. + + def __len__(self): + return len(self._children) + + def __nonzero__(self): + import warnings + warnings.warn( + "The behavior of this method will change in future versions. " + "Use specific 'len(elem)' or 'elem is not None' test instead.", + FutureWarning + ) + return len(self._children) != 0 # emulate old behaviour + + ## + # Returns the given subelement. + # + # @param index What subelement to return. + # @return The given subelement. + # @exception IndexError If the given element does not exist. + + def __getitem__(self, index): + return self._children[index] + + ## + # Replaces the given subelement. + # + # @param index What subelement to replace. + # @param element The new element value. + # @exception IndexError If the given element does not exist. + # @exception AssertionError If element is not a valid object. + + def __setitem__(self, index, element): + assert iselement(element) + self._children[index] = element + + ## + # Deletes the given subelement. + # + # @param index What subelement to delete. + # @exception IndexError If the given element does not exist. + + def __delitem__(self, index): + del self._children[index] + + ## + # Returns a list containing subelements in the given range. + # + # @param start The first subelement to return. + # @param stop The first subelement that shouldn't be returned. + # @return A sequence object containing subelements. + + def __getslice__(self, start, stop): + return self._children[start:stop] + + ## + # Replaces a number of subelements with elements from a sequence. + # + # @param start The first subelement to replace. + # @param stop The first subelement that shouldn't be replaced. + # @param elements A sequence object with zero or more elements. + # @exception AssertionError If a sequence member is not a valid object. + + def __setslice__(self, start, stop, elements): + for element in elements: + assert iselement(element) + self._children[start:stop] = list(elements) + + ## + # Deletes a number of subelements. + # + # @param start The first subelement to delete. + # @param stop The first subelement to leave in there. + + def __delslice__(self, start, stop): + del self._children[start:stop] + + ## + # Adds a subelement to the end of this element. + # + # @param element The element to add. + # @exception AssertionError If a sequence member is not a valid object. + + def append(self, element): + assert iselement(element) + self._children.append(element) + + ## + # Appends subelements from a sequence. + # + # @param elements A sequence object with zero or more elements. + # @exception AssertionError If a subelement is not a valid object. + # @since 1.3 + + def extend(self, elements): + for element in elements: + assert iselement(element) + self._children.extend(elements) + + ## + # Inserts a subelement at the given position in this element. + # + # @param index Where to insert the new subelement. + # @exception AssertionError If the element is not a valid object. + + def insert(self, index, element): + assert iselement(element) + self._children.insert(index, element) + + ## + # Removes a matching subelement. Unlike the find methods, + # this method compares elements based on identity, not on tag + # value or contents. + # + # @param element What element to remove. + # @exception ValueError If a matching element could not be found. + # @exception AssertionError If the element is not a valid object. + + def remove(self, element): + assert iselement(element) + self._children.remove(element) + + ## + # (Deprecated) Returns all subelements. The elements are returned + # in document order. + # + # @return A list of subelements. + # @defreturn list of Element instances + + def getchildren(self): + import warnings + warnings.warn( + "This method will be removed in future versions. " + "Use 'list(elem)' or iteration over elem instead.", + DeprecationWarning + ) + return self._children + + ## + # Finds the first matching subelement, by tag name or path. + # + # @param path What element to look for. + # @return The first matching element, or None if no element was found. + # @defreturn Element or None + + def find(self, path): + return ElementPath.find(self, path) + + ## + # Finds text for the first matching subelement, by tag name or path. + # + # @param path What element to look for. + # @param default What to return if the element was not found. + # @return The text content of the first matching element, or the + # default value no element was found. Note that if the element + # has is found, but has no text content, this method returns an + # empty string. + # @defreturn string + + def findtext(self, path, default=None): + return ElementPath.findtext(self, path, default) + + ## + # Finds all matching subelements, by tag name or path. + # + # @param path What element to look for. + # @return A list or iterator containing all matching elements, + # in document order. + # @defreturn list of Element instances + + def findall(self, path): + return ElementPath.findall(self, path) + + ## + # Resets an element. This function removes all subelements, clears + # all attributes, and sets the text and tail attributes to None. + + def clear(self): + self.attrib.clear() + self._children = [] + self.text = self.tail = None + + ## + # Gets an element attribute. + # + # @param key What attribute to look for. + # @param default What to return if the attribute was not found. + # @return The attribute value, or the default value, if the + # attribute was not found. + # @defreturn string or None + + def get(self, key, default=None): + return self.attrib.get(key, default) + + ## + # Sets an element attribute. + # + # @param key What attribute to set. + # @param value The attribute value. + + def set(self, key, value): + self.attrib[key] = value + + ## + # Gets a list of attribute names. The names are returned in an + # arbitrary order (just like for an ordinary Python dictionary). + # + # @return A list of element attribute names. + # @defreturn list of strings + + def keys(self): + return self.attrib.keys() + + ## + # Gets element attributes, as a sequence. The attributes are + # returned in an arbitrary order. + # + # @return A list of (name, value) tuples for all attributes. + # @defreturn list of (string, string) tuples + + def items(self): + return self.attrib.items() + + ## + # Creates a tree iterator. The iterator loops over this element + # and all subelements, in document order, and returns all elements + # with a matching tag. + #

+ # If the tree structure is modified during iteration, new or removed + # elements may or may not be included. To get a stable set, use the + # list() function on the iterator, and loop over the resulting list. + # + # @param tag What tags to look for (default is to return all elements). + # @return An iterator containing all the matching elements. + # @defreturn iterator + + def iter(self, tag=None): + if tag == "*": + tag = None + if tag is None or self.tag == tag: + yield self + for e in self._children: + for e in e.iter(tag): + yield e + + # compatibility (FIXME: preserve list behaviour too? see below) + getiterator = iter + + # def getiterator(self, tag=None): + # return list(tag) + + ## + # Creates a text iterator. The iterator loops over this element + # and all subelements, in document order, and returns all inner + # text. + # + # @return An iterator containing all inner text. + # @defreturn iterator + + def itertext(self): + if self.text: + yield self.text + for e in self: + for s in e.itertext(): + yield s + if e.tail: + yield e.tail + +# compatibility +_Element = _ElementInterface = Element + +## +# Subelement factory. This function creates an element instance, and +# appends it to an existing element. +#

+# The element name, attribute names, and attribute values can be +# either 8-bit ASCII strings or Unicode strings. +# +# @param parent The parent element. +# @param tag The subelement name. +# @param attrib An optional dictionary, containing element attributes. +# @param **extra Additional attributes, given as keyword arguments. +# @return An element instance. +# @defreturn Element + +def SubElement(parent, tag, attrib={}, **extra): + attrib = attrib.copy() + attrib.update(extra) + element = parent.makeelement(tag, attrib) + parent.append(element) + return element + +## +# Comment element factory. This factory function creates a special +# element that will be serialized as an XML comment by the standard +# serializer. +#

+# The comment string can be either an 8-bit ASCII string or a Unicode +# string. +# +# @param text A string containing the comment string. +# @return An element instance, representing a comment. +# @defreturn Element + +def Comment(text=None): + element = Element(Comment) + element.text = text + return element + +## +# PI element factory. This factory function creates a special element +# that will be serialized as an XML processing instruction by the standard +# serializer. +# +# @param target A string containing the PI target. +# @param text A string containing the PI contents, if any. +# @return An element instance, representing a PI. +# @defreturn Element + +def ProcessingInstruction(target, text=None): + element = Element(ProcessingInstruction) + element.text = target + if text: + element.text = element.text + " " + text + return element + +PI = ProcessingInstruction + +## +# QName wrapper. This can be used to wrap a QName attribute value, in +# order to get proper namespace handling on output. +# +# @param text A string containing the QName value, in the form {uri}local, +# or, if the tag argument is given, the URI part of a QName. +# @param tag Optional tag. If given, the first argument is interpreted as +# an URI, and this argument is interpreted as a local name. +# @return An opaque object, representing the QName. + +class QName(object): + def __init__(self, text_or_uri, tag=None): + if tag: + text_or_uri = "{%s}%s" % (text_or_uri, tag) + self.text = text_or_uri + def __str__(self): + return self.text + def __hash__(self): + return hash(self.text) + def __cmp__(self, other): + if isinstance(other, QName): + return cmp(self.text, other.text) + return cmp(self.text, other) + +# -------------------------------------------------------------------- + +## +# ElementTree wrapper class. This class represents an entire element +# hierarchy, and adds some extra support for serialization to and from +# standard XML. +# +# @param element Optional root element. +# @keyparam file Optional file handle or file name. If given, the +# tree is initialized with the contents of this XML file. + +class ElementTree(object): + + def __init__(self, element=None, file=None): + assert element is None or iselement(element) + self._root = element # first node + if file: + self.parse(file) + + ## + # Gets the root element for this tree. + # + # @return An element instance. + # @defreturn Element + + def getroot(self): + return self._root + + ## + # Replaces the root element for this tree. This discards the + # current contents of the tree, and replaces it with the given + # element. Use with care. + # + # @param element An element instance. + + def _setroot(self, element): + assert iselement(element) + self._root = element + + ## + # Loads an external XML document into this element tree. + # + # @param source A file name or file object. + # @keyparam parser An optional parser instance. If not given, the + # standard {@link XMLParser} parser is used. + # @return The document root element. + # @defreturn Element + + def parse(self, source, parser=None): + if not hasattr(source, "read"): + source = open(source, "rb") + if not parser: + parser = XMLParser(target=TreeBuilder()) + while 1: + data = source.read(32768) + if not data: + break + parser.feed(data) + self._root = parser.close() + return self._root + + ## + # Creates a tree iterator for the root element. The iterator loops + # over all elements in this tree, in document order. + # + # @param tag What tags to look for (default is to return all elements) + # @return An iterator. + # @defreturn iterator + + def iter(self, tag=None): + assert self._root is not None + return self._root.iter(tag) + + getiterator = iter + + ## + # Finds the first toplevel element with given tag. + # Same as getroot().find(path). + # + # @param path What element to look for. + # @return The first matching element, or None if no element was found. + # @defreturn Element or None + + def find(self, path): + assert self._root is not None + if path[:1] == "/": + path = "." + path + import warnings + warnings.warn( + "This search is broken in 1.3 and earlier; if you rely " + "on the current behaviour, change it to %r" % path, + FutureWarning + ) + return self._root.find(path) + + ## + # Finds the element text for the first toplevel element with given + # tag. Same as getroot().findtext(path). + # + # @param path What toplevel element to look for. + # @param default What to return if the element was not found. + # @return The text content of the first matching element, or the + # default value no element was found. Note that if the element + # has is found, but has no text content, this method returns an + # empty string. + # @defreturn string + + def findtext(self, path, default=None): + assert self._root is not None + if path[:1] == "/": + path = "." + path + import warnings + warnings.warn( + "This search is broken in 1.3 and earlier; if you rely " + "on the current behaviour, change it to %r" % path, + FutureWarning + ) + return self._root.findtext(path, default) + + ## + # Finds all toplevel elements with the given tag. + # Same as getroot().findall(path). + # + # @param path What element to look for. + # @return A list or iterator containing all matching elements, + # in document order. + # @defreturn list of Element instances + + def findall(self, path): + assert self._root is not None + if path[:1] == "/": + path = "." + path + import warnings + warnings.warn( + "This search is broken in 1.3 and earlier; if you rely " + "on the current behaviour, change it to %r" % path, + FutureWarning + ) + return self._root.findall(path) + + ## + # Writes the element tree to a file, as XML. + # + # @param file A file name, or a file object opened for writing. + # @keyparam encoding Optional output encoding (default is US-ASCII). + # @keyparam method Optional output method ("xml" or "html"; default + # is "xml". + # @keyparam xml_declaration Controls if an XML declaration should + # be added to the file. Use False for never, True for always, + # None for only if not US-ASCII or UTF-8. None is default. + + def write(self, file, + # keyword arguments + encoding="us-ascii", + xml_declaration=None, + default_namespace=None, + method=None): + assert self._root is not None + if not hasattr(file, "write"): + file = open(file, "wb") + write = file.write + if not method: + method = "xml" + if not encoding: + encoding = "us-ascii" + elif xml_declaration or (xml_declaration is None and + encoding not in ("utf-8", "us-ascii")): + write("\n" % encoding) + if method == "text": + _serialize_text(write, self._root, encoding) + else: + qnames, namespaces = _namespaces( + self._root, encoding, default_namespace + ) + if method == "xml": + _serialize_xml( + write, self._root, encoding, qnames, namespaces + ) + elif method == "html": + _serialize_html( + write, self._root, encoding, qnames, namespaces + ) + else: + raise ValueError("unknown method %r" % method) + +# -------------------------------------------------------------------- +# serialization support + +def _namespaces(elem, encoding, default_namespace=None): + # identify namespaces used in this tree + + # maps qnames to *encoded* prefix:local names + qnames = {None: None} + + # maps uri:s to prefixes + namespaces = {} + if default_namespace: + namespaces[default_namespace] = "" + + def encode(text): + return text.encode(encoding) + + def add_qname(qname): + # calculate serialized qname representation + try: + if qname[:1] == "{": + uri, tag = qname[1:].split("}", 1) + prefix = namespaces.get(uri) + if prefix is None: + prefix = _namespace_map.get(uri) + if prefix is None: + prefix = "ns%d" % len(namespaces) + if prefix != "xml": + namespaces[uri] = prefix + if prefix: + qnames[qname] = encode("%s:%s" % (prefix, tag)) + else: + qnames[qname] = encode(tag) # default element + else: + if default_namespace: + # FIXME: can this be handled in XML 1.0? + raise ValueError( + "cannot use non-qualified names with " + "default_namespace option" + ) + qnames[qname] = encode(qname) + except TypeError: + _raise_serialization_error(qname) + + # populate qname and namespaces table + try: + iterate = elem.iter + except AttributeError: + iterate = elem.getiterator # cET compatibility + for elem in iterate(): + tag = elem.tag + if isinstance(tag, QName) and tag.text not in qnames: + add_qname(tag.text) + elif isinstance(tag, basestring): + if tag not in qnames: + add_qname(tag) + elif tag is not None and tag is not Comment and tag is not PI: + _raise_serialization_error(tag) + for key, value in elem.items(): + if isinstance(key, QName): + key = key.text + if key not in qnames: + add_qname(key) + if isinstance(value, QName) and value.text not in qnames: + add_qname(value.text) + text = elem.text + if isinstance(text, QName) and text.text not in qnames: + add_qname(text.text) + return qnames, namespaces + +def _serialize_xml(write, elem, encoding, qnames, namespaces): + tag = elem.tag + text = elem.text + if tag is Comment: + write("" % _escape_cdata(text, encoding)) + elif tag is ProcessingInstruction: + write("" % _escape_cdata(text, encoding)) + else: + tag = qnames[tag] + if tag is None: + if text: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_xml(write, e, encoding, qnames, None) + else: + write("<" + tag) + items = elem.items() + if items or namespaces: + items.sort() # lexical order + for k, v in items: + if isinstance(k, QName): + k = k.text + if isinstance(v, QName): + v = qnames[v.text] + else: + v = _escape_attrib(v, encoding) + write(" %s=\"%s\"" % (qnames[k], v)) + if namespaces: + items = namespaces.items() + items.sort(key=lambda x: x[1]) # sort on prefix + for v, k in items: + if k: + k = ":" + k + write(" xmlns%s=\"%s\"" % ( + k.encode(encoding), + _escape_attrib(v, encoding) + )) + if text or len(elem): + write(">") + if text: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_xml(write, e, encoding, qnames, None) + write("") + else: + write(" />") + if elem.tail: + write(_escape_cdata(elem.tail, encoding)) + +HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr", + "img", "input", "isindex", "link", "meta" "param") + +try: + HTML_EMPTY = set(HTML_EMPTY) +except NameError: + pass + +def _serialize_html(write, elem, encoding, qnames, namespaces): + tag = elem.tag + text = elem.text + if tag is Comment: + write("" % _escape_cdata(text, encoding)) + elif tag is ProcessingInstruction: + write("" % _escape_cdata(text, encoding)) + else: + tag = qnames[tag] + if tag is None: + if text: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_html(write, e, encoding, qnames, None) + else: + write("<" + tag) + items = elem.items() + if items or namespaces: + items.sort() # lexical order + for k, v in items: + if isinstance(k, QName): + k = k.text + if isinstance(v, QName): + v = qnames[v.text] + else: + v = _escape_attrib_html(v, encoding) + # FIXME: handle boolean attributes + write(" %s=\"%s\"" % (qnames[k], v)) + if namespaces: + items = namespaces.items() + items.sort(key=lambda x: x[1]) # sort on prefix + for v, k in items: + if k: + k = ":" + k + write(" xmlns%s=\"%s\"" % ( + k.encode(encoding), + _escape_attrib(v, encoding) + )) + write(">") + tag = tag.lower() + if text: + if tag == "script" or tag == "style": + write(_encode(text, encoding)) + else: + write(_escape_cdata(text, encoding)) + for e in elem: + _serialize_html(write, e, encoding, qnames, None) + if tag not in HTML_EMPTY: + write("") + if elem.tail: + write(_escape_cdata(elem.tail, encoding)) + +def _serialize_text(write, elem, encoding): + for part in elem.itertext(): + write(part.encode(encoding)) + if elem.tail: + write(elem.tail.encode(encoding)) + +## +# Registers a namespace prefix. The registry is global, and any +# existing mapping for either the given prefix or the namespace URI +# will be removed. +# +# @param prefix Namespace prefix. +# @param uri Namespace uri. Tags and attributes in this namespace +# will be serialized with the given prefix, if at all possible. +# @raise ValueError If the prefix is reserved, or is otherwise +# invalid. + +def register_namespace(prefix, uri): + if re.match("ns\d+$", prefix): + raise ValueError("Prefix format reserved for internal use") + for k, v in _namespace_map.items(): + if k == uri or v == prefix: + del _namespace_map[k] + _namespace_map[uri] = prefix + +_namespace_map = { + # "well-known" namespace prefixes + "http://www.w3.org/XML/1998/namespace": "xml", + "http://www.w3.org/1999/xhtml": "html", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", + "http://schemas.xmlsoap.org/wsdl/": "wsdl", + # xml schema + "http://www.w3.org/2001/XMLSchema": "xs", + "http://www.w3.org/2001/XMLSchema-instance": "xsi", + # dublic core + "http://purl.org/dc/elements/1.1/": "dc", +} + +def _raise_serialization_error(text): + raise TypeError( + "cannot serialize %r (type %s)" % (text, type(text).__name__) + ) + +def _encode(text, encoding): + try: + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def _escape_cdata(text, encoding): + # escape character data + try: + # it's worth avoiding do-nothing calls for strings that are + # shorter than 500 character, or so. assume that's, by far, + # the most common case in most applications. + if "&" in text: + text = text.replace("&", "&") + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def _escape_attrib(text, encoding): + # escape attribute value + try: + if "&" in text: + text = text.replace("&", "&") + if "<" in text: + text = text.replace("<", "<") + if ">" in text: + text = text.replace(">", ">") + if "\"" in text: + text = text.replace("\"", """) + if "\n" in text: + text = text.replace("\n", " ") + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def _escape_attrib_html(text, encoding): + # escape attribute value + try: + if "&" in text: + text = text.replace("&", "&") + if ">" in text: + text = text.replace(">", ">") + if "\"" in text: + text = text.replace("\"", """) + return text.encode(encoding, "xmlcharrefreplace") + except (TypeError, AttributeError): + _raise_serialization_error(text) + +# -------------------------------------------------------------------- + +## +# Generates a string representation of an XML element, including all +# subelements. +# +# @param element An Element instance. +# @return An encoded string containing the XML data. +# @defreturn string + +def tostring(element, encoding=None, method=None): + class dummy: + pass + data = [] + file = dummy() + file.write = data.append + ElementTree(element).write(file, encoding, method=method) + return "".join(data) + +## +# Generates a string representation of an XML element, including all +# subelements. The string is returned as a sequence of string fragments. +# +# @param element An Element instance. +# @return A sequence object containing the XML data. +# @defreturn sequence +# @since 1.3 + +def tostringlist(element, encoding=None): + class dummy: + pass + data = [] + file = dummy() + file.write = data.append + ElementTree(element).write(file, encoding) + # FIXME: merge small fragments into larger parts + return data + +## +# Writes an element tree or element structure to sys.stdout. This +# function should be used for debugging only. +#

+# The exact output format is implementation dependent. In this +# version, it's written as an ordinary XML file. +# +# @param elem An element tree or an individual element. + +def dump(elem): + # debugging + if not isinstance(elem, ElementTree): + elem = ElementTree(elem) + elem.write(sys.stdout) + tail = elem.getroot().tail + if not tail or tail[-1] != "\n": + sys.stdout.write("\n") + +# -------------------------------------------------------------------- +# parsing + +## +# Parses an XML document into an element tree. +# +# @param source A filename or file object containing XML data. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return An ElementTree instance + +def parse(source, parser=None): + tree = ElementTree() + tree.parse(source, parser) + return tree + +## +# Parses an XML document into an element tree incrementally, and reports +# what's going on to the user. +# +# @param source A filename or file object containing XML data. +# @param events A list of events to report back. If omitted, only "end" +# events are reported. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return A (event, elem) iterator. + +def iterparse(source, events=None, parser=None): + if not hasattr(source, "read"): + source = open(source, "rb") + if not parser: + parser = XMLParser(target=TreeBuilder()) + return _IterParseIterator(source, events, parser) + +class _IterParseIterator(object): + + def __init__(self, source, events, parser): + self._file = source + self._events = [] + self._index = 0 + self.root = self._root = None + self._parser = parser + # wire up the parser for event reporting + parser = self._parser._parser + append = self._events.append + if events is None: + events = ["end"] + for event in events: + if event == "start": + try: + parser.ordered_attributes = 1 + parser.specified_attributes = 1 + def handler(tag, attrib_in, event=event, append=append, + start=self._parser._start_list): + append((event, start(tag, attrib_in))) + parser.StartElementHandler = handler + except AttributeError: + def handler(tag, attrib_in, event=event, append=append, + start=self._parser._start): + append((event, start(tag, attrib_in))) + parser.StartElementHandler = handler + elif event == "end": + def handler(tag, event=event, append=append, + end=self._parser._end): + append((event, end(tag))) + parser.EndElementHandler = handler + elif event == "start-ns": + def handler(prefix, uri, event=event, append=append): + try: + uri = uri.encode("ascii") + except UnicodeError: + pass + append((event, (prefix or "", uri))) + parser.StartNamespaceDeclHandler = handler + elif event == "end-ns": + def handler(prefix, event=event, append=append): + append((event, None)) + parser.EndNamespaceDeclHandler = handler + + def next(self): + while 1: + try: + item = self._events[self._index] + except IndexError: + if self._parser is None: + self.root = self._root + raise StopIteration + # load event buffer + del self._events[:] + self._index = 0 + data = self._file.read(16384) + if data: + self._parser.feed(data) + else: + self._root = self._parser.close() + self._parser = None + else: + self._index = self._index + 1 + return item + + def __iter__(self): + return self + +## +# Parses an XML document from a string constant. This function can +# be used to embed "XML literals" in Python code. +# +# @param source A string containing XML data. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return An Element instance. +# @defreturn Element + +def XML(text, parser=None): + if not parser: + parser = XMLParser(target=TreeBuilder()) + parser.feed(text) + return parser.close() + +## +# Parses an XML document from a string constant, and also returns +# a dictionary which maps from element id:s to elements. +# +# @param source A string containing XML data. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return A tuple containing an Element instance and a dictionary. +# @defreturn (Element, dictionary) + +def XMLID(text, parser=None): + if not parser: + parser = XMLParser(target=TreeBuilder()) + parser.feed(text) + tree = parser.close() + ids = {} + for elem in tree.getiterator(): + id = elem.get("id") + if id: + ids[id] = elem + return tree, ids + +## +# Parses an XML document from a string constant. Same as {@link #XML}. +# +# @def fromstring(text) +# @param source A string containing XML data. +# @return An Element instance. +# @defreturn Element + +fromstring = XML + +## +# Parses an XML document from a sequence of string fragments. +# +# @param sequence A list or other sequence containing XML data fragments. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLParser} parser is used. +# @return An Element instance. +# @defreturn Element +# @since 1.3 + +def fromstringlist(sequence, parser=None): + if not parser: + parser = XMLParser(target=TreeBuilder()) + for text in sequence: + parser.feed(text) + return parser.close() + +# -------------------------------------------------------------------- + +## +# Generic element structure builder. This builder converts a sequence +# of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link +# #TreeBuilder.end} method calls to a well-formed element structure. +#

+# You can use this class to build an element structure using a custom XML +# parser, or a parser for some other XML-like format. +# +# @param element_factory Optional element factory. This factory +# is called to create new Element instances, as necessary. + +class TreeBuilder(object): + + def __init__(self, element_factory=None): + self._data = [] # data collector + self._elem = [] # element stack + self._last = None # last element + self._tail = None # true if we're after an end tag + if element_factory is None: + element_factory = Element + self._factory = element_factory + + ## + # Flushes the builder buffers, and returns the toplevel document + # element. + # + # @return An Element instance. + # @defreturn Element + + def close(self): + assert len(self._elem) == 0, "missing end tags" + assert self._last != None, "missing toplevel element" + return self._last + + def _flush(self): + if self._data: + if self._last is not None: + text = "".join(self._data) + if self._tail: + assert self._last.tail is None, "internal error (tail)" + self._last.tail = text + else: + assert self._last.text is None, "internal error (text)" + self._last.text = text + self._data = [] + + ## + # Adds text to the current element. + # + # @param data A string. This should be either an 8-bit string + # containing ASCII text, or a Unicode string. + + def data(self, data): + self._data.append(data) + + ## + # Opens a new element. + # + # @param tag The element name. + # @param attrib A dictionary containing element attributes. + # @return The opened element. + # @defreturn Element + + def start(self, tag, attrs): + self._flush() + self._last = elem = self._factory(tag, attrs) + if self._elem: + self._elem[-1].append(elem) + self._elem.append(elem) + self._tail = 0 + return elem + + ## + # Closes the current element. + # + # @param tag The element name. + # @return The closed element. + # @defreturn Element + + def end(self, tag): + self._flush() + self._last = self._elem.pop() + assert self._last.tag == tag,\ + "end tag mismatch (expected %s, got %s)" % ( + self._last.tag, tag) + self._tail = 1 + return self._last + +## +# Element structure builder for XML source data, based on the +# expat parser. +# +# @keyparam target Target object. If omitted, the builder uses an +# instance of the standard {@link #TreeBuilder} class. +# @keyparam html Predefine HTML entities. This flag is not supported +# by the current implementation. +# @keyparam encoding Optional encoding. If given, the value overrides +# the encoding specified in the XML file. +# @see #ElementTree +# @see #TreeBuilder + +class XMLParser(object): + + def __init__(self, html=0, target=None, encoding=None): + try: + from xml.parsers import expat + except ImportError: + try: + import pyexpat; expat = pyexpat + except ImportError: + raise ImportError( + "No module named expat; use SimpleXMLTreeBuilder instead" + ) + parser = expat.ParserCreate(encoding, "}") + if target is None: + target = TreeBuilder() + # underscored names are provided for compatibility only + self.parser = self._parser = parser + self.target = self._target = target + self._error = expat.error + self._names = {} # name memo cache + # callbacks + parser.DefaultHandlerExpand = self._default + parser.StartElementHandler = self._start + parser.EndElementHandler = self._end + parser.CharacterDataHandler = self._data + # let expat do the buffering, if supported + try: + self._parser.buffer_text = 1 + except AttributeError: + pass + # use new-style attribute handling, if supported + try: + self._parser.ordered_attributes = 1 + self._parser.specified_attributes = 1 + parser.StartElementHandler = self._start_list + except AttributeError: + pass + self._doctype = None + self.entity = {} + try: + self.version = "Expat %d.%d.%d" % expat.version_info + except AttributeError: + pass # unknown + + def _raiseerror(self, value): + err = ParseError(value) + err.code = value.code + err.position = value.lineno, value.offset + raise err + + def _fixtext(self, text): + # convert text string to ascii, if possible + try: + return text.encode("ascii") + except UnicodeError: + return text + + def _fixname(self, key): + # expand qname, and convert name string to ascii, if possible + try: + name = self._names[key] + except KeyError: + name = key + if "}" in name: + name = "{" + name + self._names[key] = name = self._fixtext(name) + return name + + def _start(self, tag, attrib_in): + fixname = self._fixname + fixtext = self._fixtext + tag = fixname(tag) + attrib = {} + for key, value in attrib_in.items(): + attrib[fixname(key)] = fixtext(value) + return self.target.start(tag, attrib) + + def _start_list(self, tag, attrib_in): + fixname = self._fixname + fixtext = self._fixtext + tag = fixname(tag) + attrib = {} + if attrib_in: + for i in range(0, len(attrib_in), 2): + attrib[fixname(attrib_in[i])] = fixtext(attrib_in[i+1]) + return self.target.start(tag, attrib) + + def _data(self, text): + return self.target.data(self._fixtext(text)) + + def _end(self, tag): + return self.target.end(self._fixname(tag)) + + def _default(self, text): + prefix = text[:1] + if prefix == "&": + # deal with undefined entities + try: + self.target.data(self.entity[text[1:-1]]) + except KeyError: + from xml.parsers import expat + err = expat.error( + "undefined entity %s: line %d, column %d" % + (text, self._parser.ErrorLineNumber, + self._parser.ErrorColumnNumber) + ) + err.code = 11 # XML_ERROR_UNDEFINED_ENTITY + err.lineno = self._parser.ErrorLineNumber + err.offset = self._parser.ErrorColumnNumber + raise err + elif prefix == "<" and text[:9] == "": + self._doctype = None + return + text = text.strip() + if not text: + return + self._doctype.append(text) + n = len(self._doctype) + if n > 2: + type = self._doctype[1] + if type == "PUBLIC" and n == 4: + name, type, pubid, system = self._doctype + elif type == "SYSTEM" and n == 3: + name, type, system = self._doctype + pubid = None + else: + return + if pubid: + pubid = pubid[1:-1] + if hasattr(self.target, "doctype"): + self.target.doctype(name, pubid, system[1:-1]) + self._doctype = None + + ## + # Feeds data to the parser. + # + # @param data Encoded data. + + def feed(self, data): + try: + self._parser.Parse(data, 0) + except self._error, v: + self._raiseerror(v) + + ## + # Finishes feeding data to the parser. + # + # @return An element structure. + # @defreturn Element + + def close(self): + try: + self._parser.Parse("", 1) # end of data + except self._error, v: + self._raiseerror(v) + tree = self.target.close() + del self.target, self._parser # get rid of circular references + return tree + +# compatibility +XMLTreeBuilder = XMLParser Added: doctools/branches/0.4.x/tests/etree13/HTMLTreeBuilder.py ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/etree13/HTMLTreeBuilder.py Fri Aug 1 21:11:22 2008 @@ -0,0 +1,230 @@ +# +# ElementTree +# $Id$ +# +# a simple tree builder, for HTML input +# +# history: +# 2002-04-06 fl created +# 2002-04-07 fl ignore IMG and HR end tags +# 2002-04-07 fl added support for 1.5.2 and later +# 2003-04-13 fl added HTMLTreeBuilder alias +# 2004-12-02 fl don't feed non-ASCII charrefs/entities as 8-bit strings +# 2004-12-05 fl don't feed non-ASCII CDATA as 8-bit strings +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik at pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to build element trees from HTML files. +## + +import htmlentitydefs +import re, string, sys +import mimetools, StringIO + +import ElementTree + +AUTOCLOSE = "p", "li", "tr", "th", "td", "head", "body" +IGNOREEND = "img", "hr", "meta", "link", "br" + +if sys.version[:3] == "1.5": + is_not_ascii = re.compile(r"[\x80-\xff]").search # 1.5.2 +else: + is_not_ascii = re.compile(eval(r'u"[\u0080-\uffff]"')).search + +try: + from HTMLParser import HTMLParser +except ImportError: + from sgmllib import SGMLParser + # hack to use sgmllib's SGMLParser to emulate 2.2's HTMLParser + class HTMLParser(SGMLParser): + # the following only works as long as this class doesn't + # provide any do, start, or end handlers + def unknown_starttag(self, tag, attrs): + self.handle_starttag(tag, attrs) + def unknown_endtag(self, tag): + self.handle_endtag(tag) + +## +# ElementTree builder for HTML source code. This builder converts an +# HTML document or fragment to an ElementTree. +#

+# The parser is relatively picky, and requires balanced tags for most +# elements. However, elements belonging to the following group are +# automatically closed: P, LI, TR, TH, and TD. In addition, the +# parser automatically inserts end tags immediately after the start +# tag, and ignores any end tags for the following group: IMG, HR, +# META, and LINK. +# +# @keyparam builder Optional builder object. If omitted, the parser +# uses the standard elementtree builder. +# @keyparam encoding Optional character encoding, if known. If omitted, +# the parser looks for META tags inside the document. If no tags +# are found, the parser defaults to ISO-8859-1. Note that if your +# document uses a non-ASCII compatible encoding, you must decode +# the document before parsing. +# +# @see elementtree.ElementTree + +class HTMLTreeBuilder(HTMLParser): + + # FIXME: shouldn't this class be named Parser, not Builder? + + def __init__(self, builder=None, encoding=None): + self.__stack = [] + if builder is None: + builder = ElementTree.TreeBuilder() + self.__builder = builder + self.encoding = encoding or "iso-8859-1" + HTMLParser.__init__(self) + + ## + # Flushes parser buffers, and return the root element. + # + # @return An Element instance. + + def close(self): + HTMLParser.close(self) + return self.__builder.close() + + ## + # (Internal) Handles start tags. + + def handle_starttag(self, tag, attrs): + if tag == "meta": + # look for encoding directives + http_equiv = content = None + for k, v in attrs: + if k == "http-equiv": + http_equiv = string.lower(v) + elif k == "content": + content = v + if http_equiv == "content-type" and content: + # use mimetools to parse the http header + header = mimetools.Message( + StringIO.StringIO("%s: %s\n\n" % (http_equiv, content)) + ) + encoding = header.getparam("charset") + if encoding: + self.encoding = encoding + if tag in AUTOCLOSE: + if self.__stack and self.__stack[-1] == tag: + self.handle_endtag(tag) + self.__stack.append(tag) + attrib = {} + if attrs: + for k, v in attrs: + attrib[string.lower(k)] = v + self.__builder.start(tag, attrib) + if tag in IGNOREEND: + self.__stack.pop() + self.__builder.end(tag) + + ## + # (Internal) Handles end tags. + + def handle_endtag(self, tag): + if tag in IGNOREEND: + return + lasttag = self.__stack.pop() + if tag != lasttag and lasttag in AUTOCLOSE: + self.handle_endtag(lasttag) + self.__builder.end(tag) + + ## + # (Internal) Handles character references. + + def handle_charref(self, char): + if char[:1] == "x": + char = int(char[1:], 16) + else: + char = int(char) + if 0 <= char < 128: + self.__builder.data(chr(char)) + else: + self.__builder.data(unichr(char)) + + ## + # (Internal) Handles entity references. + + def handle_entityref(self, name): + entity = htmlentitydefs.entitydefs.get(name) + if entity: + if len(entity) == 1: + entity = ord(entity) + else: + entity = int(entity[2:-1]) + if 0 <= entity < 128: + self.__builder.data(chr(entity)) + else: + self.__builder.data(unichr(entity)) + else: + self.unknown_entityref(name) + + ## + # (Internal) Handles character data. + + def handle_data(self, data): + if isinstance(data, type('')) and is_not_ascii(data): + # convert to unicode, but only if necessary + data = unicode(data, self.encoding, "ignore") + self.__builder.data(data) + + ## + # (Hook) Handles unknown entity references. The default action + # is to ignore unknown entities. + + def unknown_entityref(self, name): + pass # ignore by default; override if necessary + +## +# An alias for the HTMLTreeBuilder class. + +TreeBuilder = HTMLTreeBuilder + +## +# Parse an HTML document or document fragment. +# +# @param source A filename or file object containing HTML data. +# @param encoding Optional character encoding, if known. If omitted, +# the parser looks for META tags inside the document. If no tags +# are found, the parser defaults to ISO-8859-1. +# @return An ElementTree instance + +def parse(source, encoding=None): + return ElementTree.parse(source, HTMLTreeBuilder(encoding=encoding)) + +if __name__ == "__main__": + import sys + ElementTree.dump(parse(open(sys.argv[1]))) Added: doctools/branches/0.4.x/tests/etree13/__init__.py ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/etree13/__init__.py Fri Aug 1 21:11:22 2008 @@ -0,0 +1,30 @@ +# $Id$ +# elementtree package + +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2007 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- Modified: doctools/branches/0.4.x/tests/root/conf.py ============================================================================== --- doctools/branches/0.4.x/tests/root/conf.py (original) +++ doctools/branches/0.4.x/tests/root/conf.py Fri Aug 1 21:11:22 2008 @@ -32,7 +32,7 @@ source_suffix = '.txt' # The master toctree document. -master_doc = 'index' +master_doc = 'contents' # General substitutions. project = 'Sphinx Tests' Modified: doctools/branches/0.4.x/tests/root/contents.txt ============================================================================== --- doctools/branches/0.4.x/tests/root/contents.txt (original) +++ doctools/branches/0.4.x/tests/root/contents.txt Fri Aug 1 21:11:22 2008 @@ -10,6 +10,8 @@ .. toctree:: :maxdepth: 2 + images + Indices and tables ================== Added: doctools/branches/0.4.x/tests/root/images.txt ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/root/images.txt Fri Aug 1 21:11:22 2008 @@ -0,0 +1,20 @@ +Sphinx image handling +===================== + +.. first, a simple test with direct filename +.. image:: img.png + +.. a non-existing image with direct filename +.. image:: foo.png + +.. an image with path name (relative to this directory!) +.. image:: subdir/img.png + +.. an image with unspecified extension +.. image:: img.* + +.. a non-existing image with .* +.. image:: foo.* + +.. a non-local image URI +.. image:: http://www.python.org/logo.png Added: doctools/branches/0.4.x/tests/root/img.gif ============================================================================== Binary file. No diff available. Added: doctools/branches/0.4.x/tests/root/img.pdf ============================================================================== Binary file. No diff available. Added: doctools/branches/0.4.x/tests/root/img.png ============================================================================== Binary file. No diff available. Added: doctools/branches/0.4.x/tests/root/subdir/img.png ============================================================================== Binary file. No diff available. Added: doctools/branches/0.4.x/tests/test_build.py ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/test_build.py Fri Aug 1 21:11:22 2008 @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +""" + test_build + ~~~~~~~~~~ + + Test the entire build process with the test root. + + :copyright: 2008 by Georg Brandl. + :license: BSD. +""" + +import os +import htmlentitydefs +from StringIO import StringIO +from etree13 import ElementTree as ET + +from util import * + +from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder + + +html_warnfile = StringIO() +latex_warnfile = StringIO() + +ENV_WARNINGS = """\ +WARNING: %(root)s/images.txt:9: Image file not readable: foo.png +WARNING: %(root)s/images.txt:20: Nonlocal image URI found: http://www.python.org/logo.png +""" + +HTML_WARNINGS = ENV_WARNINGS + """\ +WARNING: %(root)s/images.txt:: no matching candidate for image URI u'foo.*' +""" + +LATEX_WARNINGS = ENV_WARNINGS + """\ +WARNING: None:: no matching candidate for image URI u'foo.*' +""" + +HTML_XPATH = { + 'images.html': { + ".//img[@src='_images/img.png']": '', + ".//img[@src='_images/img1.png']": '', + } +} + +class NslessParser(ET.XMLParser): + def _fixname(self, key): + try: + return self._names[key] + except KeyError: + name = key + br = name.find('}') + if br > 0: + name = name[br+1:] + self._names[key] = name = self._fixtext(name) + return name + + + at with_testapp(buildername='html', warning=html_warnfile) +def test_html(app): + app.builder.build_all() + html_warnings = html_warnfile.getvalue().replace(os.sep, '/') + assert html_warnings == HTML_WARNINGS % {'root': app.srcdir} + + if not ET: + return + parser = NslessParser() + parser.entity.update(htmlentitydefs.entitydefs) + for fname, paths in HTML_XPATH.iteritems(): + etree = ET.parse(app.outdir / fname, parser) + for path, text in paths.iteritems(): + nodes = list(etree.findall(path)) + assert nodes != [] + for node in nodes: + if text: assert text in node.text + + + at with_testapp(buildername='latex', warning=latex_warnfile) +def test_latex(app): + app.builder.build_all() + latex_warnings = latex_warnfile.getvalue().replace(os.sep, '/') + assert latex_warnings == LATEX_WARNINGS % {'root': app.srcdir} Added: doctools/branches/0.4.x/tests/test_env.py ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/test_env.py Fri Aug 1 21:11:22 2008 @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +""" + test_env + ~~~~~~~~ + + Test the BuildEnvironment class. + + :copyright: 2008 by Georg Brandl. + :license: BSD. +""" + +from util import * + +from sphinx.environment import BuildEnvironment +from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder + +app = env = None +warnings = [] + +def setup_module(): + global app, env + app = TestApp(srcdir='(temp)') + env = BuildEnvironment(app.srcdir, app.doctreedir, app.config) + env.set_warnfunc(warnings.append) + +def teardown_module(): + app.cleanup() + +def warning_emitted(file, text): + for warning in warnings: + if file+':' in warning and text in warning: + return True + return False + +# Tests are run in the order they appear in the file, therefore we can +# afford to not run update() in the setup but in its own test + +def test_first_update(): + it = env.update(app.config, app.srcdir, app.doctreedir, app) + msg = it.next() + assert msg.endswith('%d added, 0 changed, 0 removed' % len(env.found_docs)) + docnames = set() + for docname in it: # the generator does all the work + docnames.add(docname) + assert docnames == env.found_docs == set(env.all_docs) + +def test_images(): + assert warning_emitted('images.txt', 'Image file not readable: foo.png') + assert warning_emitted('images.txt', 'Nonlocal image URI found: ' + 'http://www.python.org/logo.png') + + tree = env.get_doctree('images') + app._warning.reset() + htmlbuilder = StandaloneHTMLBuilder(app, env) + htmlbuilder.post_process_images(tree) + assert "no matching candidate for image URI u'foo.*'" in app._warning.content[-1] + assert set(htmlbuilder.images.keys()) == set(['subdir/img.png', 'img.png']) + assert set(htmlbuilder.images.values()) == set(['img.png', 'img1.png']) + + app._warning.reset() + latexbuilder = LaTeXBuilder(app, env) + latexbuilder.post_process_images(tree) + assert "no matching candidate for image URI u'foo.*'" in app._warning.content[-1] + assert set(latexbuilder.images.keys()) == set(['subdir/img.png', 'img.png', 'img.pdf']) + assert set(latexbuilder.images.values()) == set(['img.pdf', 'img.png', 'img1.png']) + +def test_second_update(): + # delete, add and edit some files and update again + root = path(app.srcdir) + (root / 'images.txt').unlink() + (root / 'contents.txt').write_text('Changed file') + (root / 'new.txt').write_text('New file\n========\n') + it = env.update(app.config, app.srcdir, app.doctreedir, app) + msg = it.next() + assert '1 added, 1 changed, 1 removed' in msg + docnames = set() + for docname in it: + docnames.add(docname) + assert docnames == set(['contents', 'new']) + assert 'images' not in env.all_docs + assert 'images' not in env.found_docs Modified: doctools/branches/0.4.x/tests/test_markup.py ============================================================================== --- doctools/branches/0.4.x/tests/test_markup.py (original) +++ doctools/branches/0.4.x/tests/test_markup.py Fri Aug 1 21:11:22 2008 @@ -84,7 +84,7 @@ # non-interpolation of dashes in option role verify(':option:`--with-option`', - '

--with-option

', + '

--with-option

', r'\emph{\texttt{--with-option}}') # verify smarty-pants quotes Modified: doctools/branches/0.4.x/tests/util.py ============================================================================== --- doctools/branches/0.4.x/tests/util.py (original) +++ doctools/branches/0.4.x/tests/util.py Fri Aug 1 21:11:22 2008 @@ -25,8 +25,9 @@ __all__ = [ 'test_root', 'raises', 'raises_msg', - 'ErrorOutput', 'TestApp', 'with_testapp', + 'ListOutput', 'TestApp', 'with_testapp', 'path', 'with_tempdir', 'write_file', + 'sprint', ] @@ -65,15 +66,19 @@ (func.__name__, _excstr(exc))) -class ErrorOutput(object): +class ListOutput(object): """ - File-like object that raises :exc:`AssertionError` on ``write()``. + File-like object that collects written text in a list. """ def __init__(self, name): self.name = name + self.content = [] + + def reset(self): + del self.content[:] def write(self, text): - assert False, 'tried to write %r to %s' % (text, self.name) + self.content.append(text) class TestApp(application.Sphinx): @@ -90,11 +95,15 @@ if srcdir is None: srcdir = test_root + if srcdir == '(temp)': + tempdir = path(tempfile.mkdtemp()) / 'root' + test_root.copytree(tempdir) + srcdir = tempdir else: srcdir = path(srcdir) self.builddir = srcdir.joinpath('_build') if not self.builddir.isdir(): - os.makedirs(self.builddir) + self.builddir.makedirs() self.made_builddir = True else: self.made_builddir = False @@ -102,6 +111,8 @@ confdir = srcdir if outdir is None: outdir = srcdir.joinpath(self.builddir, buildername) + if not outdir.isdir(): + outdir.makedirs() if doctreedir is None: doctreedir = srcdir.joinpath(srcdir, self.builddir, 'doctrees') if confoverrides is None: @@ -109,7 +120,7 @@ if status is None: status = StringIO.StringIO() if warning is None: - warning = ErrorOutput('stderr') + warning = ListOutput('stderr') if freshenv is None: freshenv = True @@ -119,10 +130,10 @@ def cleanup(self): trees = [self.outdir, self.doctreedir] - if self.made_builddir: - trees.append(self.builddir) - for tree in trees: - shutil.rmtree(tree, True) + #if self.made_builddir: + # trees.append(self.builddir) + #for tree in trees: + # shutil.rmtree(tree, True) def with_testapp(*args, **kwargs): @@ -155,3 +166,7 @@ f = open(str(name), 'wb') f.write(contents) f.close() + + +def sprint(*args): + sys.stderr.write(' '.join(map(str, args)) + '\n') From python-checkins at python.org Fri Aug 1 21:28:34 2008 From: python-checkins at python.org (georg.brandl) Date: Fri, 1 Aug 2008 21:28:34 +0200 (CEST) Subject: [Python-checkins] r65373 - doctools/branches/0.4.x/tests/util.py Message-ID: <20080801192834.57D0D1E4016@bag.python.org> Author: georg.brandl Date: Fri Aug 1 21:28:33 2008 New Revision: 65373 Log: Fix oversight. Modified: doctools/branches/0.4.x/tests/util.py Modified: doctools/branches/0.4.x/tests/util.py ============================================================================== --- doctools/branches/0.4.x/tests/util.py (original) +++ doctools/branches/0.4.x/tests/util.py Fri Aug 1 21:28:33 2008 @@ -130,10 +130,10 @@ def cleanup(self): trees = [self.outdir, self.doctreedir] - #if self.made_builddir: - # trees.append(self.builddir) - #for tree in trees: - # shutil.rmtree(tree, True) + if self.made_builddir: + trees.append(self.builddir) + for tree in trees: + shutil.rmtree(tree, True) def with_testapp(*args, **kwargs): From python-checkins at python.org Fri Aug 1 21:36:33 2008 From: python-checkins at python.org (benjamin.peterson) Date: Fri, 1 Aug 2008 21:36:33 +0200 (CEST) Subject: [Python-checkins] r65374 - doctools/branches/0.4.x/tests/test_markup.py Message-ID: <20080801193633.03DDE1E4006@bag.python.org> Author: benjamin.peterson Date: Fri Aug 1 21:36:32 2008 New Revision: 65374 Log: fix one broken test Modified: doctools/branches/0.4.x/tests/test_markup.py Modified: doctools/branches/0.4.x/tests/test_markup.py ============================================================================== --- doctools/branches/0.4.x/tests/test_markup.py (original) +++ doctools/branches/0.4.x/tests/test_markup.py Fri Aug 1 21:36:32 2008 @@ -84,7 +84,7 @@ # non-interpolation of dashes in option role verify(':option:`--with-option`', - '

--with-option

', + '

--with-option

', r'\emph{\texttt{--with-option}}') # verify smarty-pants quotes From python-checkins at python.org Fri Aug 1 21:41:11 2008 From: python-checkins at python.org (georg.brandl) Date: Fri, 1 Aug 2008 21:41:11 +0200 (CEST) Subject: [Python-checkins] r65375 - in doctools/branches/0.4.x: CHANGES sphinx/quickstart.py sphinx/util/__init__.py tests/test_quickstart.py Message-ID: <20080801194111.CF6971E4006@bag.python.org> Author: georg.brandl Date: Fri Aug 1 21:41:11 2008 New Revision: 65375 Log: Fix the handling of non-ASCII input in quickstart. Modified: doctools/branches/0.4.x/CHANGES doctools/branches/0.4.x/sphinx/quickstart.py doctools/branches/0.4.x/sphinx/util/__init__.py doctools/branches/0.4.x/tests/test_quickstart.py Modified: doctools/branches/0.4.x/CHANGES ============================================================================== --- doctools/branches/0.4.x/CHANGES (original) +++ doctools/branches/0.4.x/CHANGES Fri Aug 1 21:41:11 2008 @@ -1,6 +1,10 @@ Release 0.4.3 (in development) ============================== +* Fix the handling of non-ASCII characters entered in quickstart. + +* Fix a crash with nonexisting image URIs. + Release 0.4.2 (Jul 29, 2008) ============================ Modified: doctools/branches/0.4.x/sphinx/quickstart.py ============================================================================== --- doctools/branches/0.4.x/sphinx/quickstart.py (original) +++ doctools/branches/0.4.x/sphinx/quickstart.py Fri Aug 1 21:41:11 2008 @@ -12,8 +12,10 @@ import sys, os, time from os import path +TERM_ENCODING = getattr(sys.stdin, 'encoding', None) + from sphinx.util import make_filename -from sphinx.util.console import purple, bold, red, nocolor +from sphinx.util.console import purple, bold, red, turquoise, nocolor PROMPT_PREFIX = '> ' @@ -56,8 +58,8 @@ master_doc = '%(master)s' # General substitutions. -project = %(project)r -copyright = '%(year)s, %(author)s' +project = u'%(project)s' +copyright = u'%(copyright)s' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. @@ -178,8 +180,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ - ('%(master)s', '%(project_fn)s.tex', '%(project)s Documentation', - '%(author)s', 'manual'), + ('%(master)s', '%(project_fn)s.tex', u'%(project_doc)s', + u'%(author)s', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -338,8 +340,18 @@ x = raw_input(prompt) if default and not x: x = default + if x.decode('ascii', 'replace').encode('ascii', 'replace') != x: + if TERM_ENCODING: + x = x.decode(TERM_ENCODING) + else: + print turquoise('* Note: non-ASCII characters entered and terminal ' + 'encoding unknown -- assuming UTF-8 or Latin-1.') + try: + x = x.decode('utf-8') + except UnicodeDecodeError: + x = x.decode('latin1') if validator and not validator(x): - print red(" * " + validator.__doc__) + print red('* ' + validator.__doc__) continue break d[key] = x @@ -409,12 +421,13 @@ os.name == 'posix' and 'y' or 'n', boolean) d['project_fn'] = make_filename(d['project']) - d['year'] = time.strftime('%Y') d['now'] = time.asctime() d['underline'] = len(d['project']) * '=' d['extensions'] = ', '.join( repr('sphinx.ext.' + name) for name in ('autodoc', 'doctest') if d['ext_' + name].upper() in ('Y', 'YES')) + d['copyright'] = time.strftime('%Y') + ', ' + d['author'] + d['project_doc'] = d['project'] + ' Documentation' if not path.isdir(d['path']): mkdir_p(d['path']) @@ -432,12 +445,12 @@ mkdir_p(path.join(srcdir, d['dot'] + 'static')) f = open(path.join(srcdir, 'conf.py'), 'w') - f.write(QUICKSTART_CONF % d) + f.write((QUICKSTART_CONF % d).encode('utf-8')) f.close() masterfile = path.join(srcdir, d['master'] + d['suffix']) f = open(masterfile, 'w') - f.write(MASTER_FILE % d) + f.write((MASTER_FILE % d).encode('utf-8')) f.close() create_makefile = d['makefile'].upper() in ('Y', 'YES') @@ -445,7 +458,7 @@ d['rsrcdir'] = separate and 'source' or '.' d['rbuilddir'] = separate and 'build' or d['dot'] + 'build' f = open(path.join(d['path'], 'Makefile'), 'w') - f.write(MAKEFILE % d) + f.write((MAKEFILE % d).encode('utf-8')) f.close() print Modified: doctools/branches/0.4.x/sphinx/util/__init__.py ============================================================================== --- doctools/branches/0.4.x/sphinx/util/__init__.py (original) +++ doctools/branches/0.4.x/sphinx/util/__init__.py Fri Aug 1 21:41:11 2008 @@ -256,7 +256,7 @@ return filter(match, names) -no_fn_re = re.compile(r'[:/\\?*%|"\'<>. \t]') +no_fn_re = re.compile(r'[^a-zA-Z0-9_-]') def make_filename(string): return no_fn_re.sub('', string) Modified: doctools/branches/0.4.x/tests/test_quickstart.py ============================================================================== --- doctools/branches/0.4.x/tests/test_quickstart.py (original) +++ doctools/branches/0.4.x/tests/test_quickstart.py Fri Aug 1 21:41:11 2008 @@ -38,6 +38,7 @@ def teardown_module(): qs.raw_input = __builtin__.raw_input + qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None) coloron() @@ -108,10 +109,10 @@ 'Root path': tempdir, 'Separate source and build': 'y', 'Name prefix for templates': '_', - 'Project name': 'Sphinx Test', - 'Author name': 'Georg Brandl', - 'Project version': '0.1', - 'Project release': '0.1.1', + 'Project name': 'STASI\xe2\x84\xa2', + 'Author name': 'Wolfgang Sch\xc3\xa4uble', + 'Project version': '2.0', + 'Project release': '2.0.1', 'Source file suffix': '.txt', 'Name of your master document': 'contents', 'autodoc': 'y', @@ -119,6 +120,7 @@ 'Create Makefile': 'no', } qs.raw_input = mock_raw_input(answers, needanswer=True) + qs.TERM_ENCODING = 'utf-8' qs.inner_main([]) conffile = tempdir / 'source' / 'conf.py' @@ -129,14 +131,14 @@ assert ns['templates_path'] == ['_templates'] assert ns['source_suffix'] == '.txt' assert ns['master_doc'] == 'contents' - assert ns['project'] == 'Sphinx Test' - assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y') - assert ns['version'] == '0.1' - assert ns['release'] == '0.1.1' + assert ns['project'] == u'STASI?' + assert ns['copyright'] == u'%s, Wolfgang Sch?uble' % time.strftime('%Y') + assert ns['version'] == '2.0' + assert ns['release'] == '2.0.1' assert ns['html_static_path'] == ['_static'] assert ns['latex_documents'] == [ - ('contents', 'SphinxTest.tex', 'Sphinx Test Documentation', - 'Georg Brandl', 'manual')] + ('contents', 'STASI.tex', u'STASI? Documentation', + u'Wolfgang Sch?uble', 'manual')] assert (tempdir / 'build').isdir() assert (tempdir / 'source' / '_static').isdir() From python-checkins at python.org Fri Aug 1 21:46:50 2008 From: python-checkins at python.org (jesse.noller) Date: Fri, 1 Aug 2008 21:46:50 +0200 (CEST) Subject: [Python-checkins] r65376 - in python/trunk/Modules/_multiprocessing: connection.h pipe_connection.c socket_connection.c Message-ID: <20080801194650.D29841E4006@bag.python.org> Author: jesse.noller Date: Fri Aug 1 21:46:50 2008 New Revision: 65376 Log: Submit fix for issue3393: Memory corruption in multiprocessing module Modified: python/trunk/Modules/_multiprocessing/connection.h python/trunk/Modules/_multiprocessing/pipe_connection.c python/trunk/Modules/_multiprocessing/socket_connection.c Modified: python/trunk/Modules/_multiprocessing/connection.h ============================================================================== --- python/trunk/Modules/_multiprocessing/connection.h (original) +++ python/trunk/Modules/_multiprocessing/connection.h Fri Aug 1 21:46:50 2008 @@ -129,9 +129,7 @@ } } - Py_BEGIN_ALLOW_THREADS res = conn_send_string(self, buffer + offset, size); - Py_END_ALLOW_THREADS if (res < 0) return mp_SetError(PyExc_IOError, res); @@ -156,10 +154,8 @@ return NULL; } - Py_BEGIN_ALLOW_THREADS res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE, &freeme, maxlength); - Py_END_ALLOW_THREADS if (res < 0) { if (res == MP_BAD_MESSAGE_LENGTH) { @@ -208,10 +204,8 @@ return NULL; } - Py_BEGIN_ALLOW_THREADS res = conn_recv_string(self, buffer+offset, length-offset, &freeme, PY_SSIZE_T_MAX); - Py_END_ALLOW_THREADS if (res < 0) { if (res == MP_BAD_MESSAGE_LENGTH) { @@ -266,9 +260,7 @@ if (PyString_AsStringAndSize(pickled_string, &buffer, &length) < 0) goto failure; - Py_BEGIN_ALLOW_THREADS res = conn_send_string(self, buffer, (int)length); - Py_END_ALLOW_THREADS if (res < 0) { mp_SetError(PyExc_IOError, res); @@ -292,10 +284,8 @@ CHECK_READABLE(self); - Py_BEGIN_ALLOW_THREADS res = conn_recv_string(self, self->buffer, CONNECTION_BUFFER_SIZE, &freeme, PY_SSIZE_T_MAX); - Py_END_ALLOW_THREADS if (res < 0) { if (res == MP_BAD_MESSAGE_LENGTH) { Modified: python/trunk/Modules/_multiprocessing/pipe_connection.c ============================================================================== --- python/trunk/Modules/_multiprocessing/pipe_connection.c (original) +++ python/trunk/Modules/_multiprocessing/pipe_connection.c Fri Aug 1 21:46:50 2008 @@ -18,9 +18,12 @@ conn_send_string(ConnectionObject *conn, char *string, size_t length) { DWORD amount_written; + BOOL ret; - return WriteFile(conn->handle, string, length, &amount_written, NULL) - ? MP_SUCCESS : MP_STANDARD_ERROR; + Py_BEGIN_ALLOW_THREADS + ret = WriteFile(conn->handle, string, length, &amount_written, NULL); + Py_END_ALLOW_THREADS + return ret ? MP_SUCCESS : MP_STANDARD_ERROR; } /* @@ -34,11 +37,14 @@ size_t buflength, char **newbuffer, size_t maxlength) { DWORD left, length, full_length, err; - + BOOL ret; *newbuffer = NULL; - if (ReadFile(conn->handle, buffer, MIN(buflength, maxlength), - &length, NULL)) + Py_BEGIN_ALLOW_THREADS + ret = ReadFile(conn->handle, buffer, MIN(buflength, maxlength), + &length, NULL); + Py_END_ALLOW_THREADS + if (ret) return length; err = GetLastError(); @@ -61,7 +67,10 @@ memcpy(*newbuffer, buffer, length); - if (ReadFile(conn->handle, *newbuffer+length, left, &length, NULL)) { + Py_BEGIN_ALLOW_THREADS + ret = ReadFile(conn->handle, *newbuffer+length, left, &length, NULL) + Py_END_ALLOW_THREADS + if (ret) { assert(length == left); return full_length; } else { Modified: python/trunk/Modules/_multiprocessing/socket_connection.c ============================================================================== --- python/trunk/Modules/_multiprocessing/socket_connection.c (original) +++ python/trunk/Modules/_multiprocessing/socket_connection.c Fri Aug 1 21:46:50 2008 @@ -73,6 +73,7 @@ static Py_ssize_t conn_send_string(ConnectionObject *conn, char *string, size_t length) { + Py_ssize_t res; /* The "header" of the message is a 32 bit unsigned number (in network order) which specifies the length of the "body". If the message is shorter than about 16kb then it is quicker to @@ -80,7 +81,6 @@ them at once. */ if (length < (16*1024)) { char *message; - int res; message = PyMem_Malloc(length+4); if (message == NULL) @@ -88,9 +88,10 @@ *(UINT32*)message = htonl((UINT32)length); memcpy(message+4, string, length); + Py_BEGIN_ALLOW_THREADS res = _conn_sendall(conn->handle, message, length+4); + Py_END_ALLOW_THREADS PyMem_Free(message); - return res; } else { UINT32 lenbuff; @@ -98,9 +99,12 @@ return MP_BAD_MESSAGE_LENGTH; lenbuff = htonl((UINT32)length); - return _conn_sendall(conn->handle, (char*)&lenbuff, 4) || + Py_BEGIN_ALLOW_THREADS + res = _conn_sendall(conn->handle, (char*)&lenbuff, 4) || _conn_sendall(conn->handle, string, length); + Py_END_ALLOW_THREADS } + return res; } /* @@ -118,7 +122,9 @@ *newbuffer = NULL; + Py_BEGIN_ALLOW_THREADS res = _conn_recvall(conn->handle, (char*)&ulength, 4); + Py_END_ALLOW_THREADS if (res < 0) return res; @@ -127,13 +133,17 @@ return MP_BAD_MESSAGE_LENGTH; if (ulength <= buflength) { + Py_BEGIN_ALLOW_THREADS res = _conn_recvall(conn->handle, buffer, (size_t)ulength); + Py_END_ALLOW_THREADS return res < 0 ? res : ulength; } else { *newbuffer = PyMem_Malloc((size_t)ulength); if (*newbuffer == NULL) return MP_MEMORY_ERROR; + Py_BEGIN_ALLOW_THREADS res = _conn_recvall(conn->handle, *newbuffer, (size_t)ulength); + Py_END_ALLOW_THREADS return res < 0 ? (Py_ssize_t)res : (Py_ssize_t)ulength; } } From python-checkins at python.org Fri Aug 1 21:48:24 2008 From: python-checkins at python.org (georg.brandl) Date: Fri, 1 Aug 2008 21:48:24 +0200 (CEST) Subject: [Python-checkins] r65377 - doctools/branches/0.4.x/tests/test_markup.py Message-ID: <20080801194824.6A75C1E4006@bag.python.org> Author: georg.brandl Date: Fri Aug 1 21:48:24 2008 New Revision: 65377 Log: Allow REs in markup checks. Modified: doctools/branches/0.4.x/tests/test_markup.py Modified: doctools/branches/0.4.x/tests/test_markup.py ============================================================================== --- doctools/branches/0.4.x/tests/test_markup.py (original) +++ doctools/branches/0.4.x/tests/test_markup.py Fri Aug 1 21:48:24 2008 @@ -9,6 +9,8 @@ :license: BSD. """ +import re + from util import * from docutils import frontend, utils, nodes @@ -43,7 +45,7 @@ pass -def verify(rst, html_expected, latex_expected): +def verify_re(rst, html_expected, latex_expected): document = utils.new_document('test data', settings) parser.parse(rst, document) for msg in document.traverse(nodes.system_message): @@ -54,14 +56,17 @@ html_translator = ForgivingHTMLTranslator(app.builder, document) document.walkabout(html_translator) html_translated = ''.join(html_translator.fragment).strip() - assert html_translated == html_expected, 'from' + rst + assert re.match(html_expected, html_translated), 'from' + rst if latex_expected: latex_translator = ForgivingLaTeXTranslator(document, app.builder) latex_translator.first_document = -1 # don't write \begin{document} document.walkabout(latex_translator) latex_translated = ''.join(latex_translator.body).strip() - assert latex_translated == latex_expected, 'from ' + rst + assert re.match(latex_expected, latex_translated), 'from ' + rst + +def verify(rst, html_expected, latex_expected): + verify_re(rst, re.escape(html_expected) + '$', re.escape(latex_expected) + '$') def test_inline(): @@ -83,9 +88,9 @@ '\\emph{a $\\rightarrow$ b}') # non-interpolation of dashes in option role - verify(':option:`--with-option`', - '

--with-option

', - r'\emph{\texttt{--with-option}}') + verify_re(':option:`--with-option`', + '

--with-option

$', + r'\\emph{\\texttt{--with-option}}$') # verify smarty-pants quotes verify('"John"', '

“John”

', "``John''") From python-checkins at python.org Fri Aug 1 22:04:44 2008 From: python-checkins at python.org (georg.brandl) Date: Fri, 1 Aug 2008 22:04:44 +0200 (CEST) Subject: [Python-checkins] r65378 - in python/trunk/Doc/reference: grammar.rst index.rst Message-ID: <20080801200444.10DD01E4006@bag.python.org> Author: georg.brandl Date: Fri Aug 1 22:04:43 2008 New Revision: 65378 Log: Add the grammar to the reference manual, since the new docs don't have the feature of putting all the small EBNF snippets together into one big file. Added: python/trunk/Doc/reference/grammar.rst Modified: python/trunk/Doc/reference/index.rst Added: python/trunk/Doc/reference/grammar.rst ============================================================================== --- (empty file) +++ python/trunk/Doc/reference/grammar.rst Fri Aug 1 22:04:43 2008 @@ -0,0 +1,7 @@ +Full Grammar specification +========================== + +This is the full Python grammar, as it is read by the parser generator and used +to parse Python source files: + +.. literalinclude:: ../../Grammar/Grammar Modified: python/trunk/Doc/reference/index.rst ============================================================================== --- python/trunk/Doc/reference/index.rst (original) +++ python/trunk/Doc/reference/index.rst Fri Aug 1 22:04:43 2008 @@ -27,4 +27,4 @@ simple_stmts.rst compound_stmts.rst toplevel_components.rst - + grammar.rst From python-checkins at python.org Fri Aug 1 22:13:29 2008 From: python-checkins at python.org (georg.brandl) Date: Fri, 1 Aug 2008 22:13:29 +0200 (CEST) Subject: [Python-checkins] r65379 - python/trunk/Doc/reference/simple_stmts.rst Message-ID: <20080801201329.688E61E4006@bag.python.org> Author: georg.brandl Date: Fri Aug 1 22:13:29 2008 New Revision: 65379 Log: This should really be a comment. Modified: python/trunk/Doc/reference/simple_stmts.rst Modified: python/trunk/Doc/reference/simple_stmts.rst ============================================================================== --- python/trunk/Doc/reference/simple_stmts.rst (original) +++ python/trunk/Doc/reference/simple_stmts.rst Fri Aug 1 22:13:29 2008 @@ -743,10 +743,13 @@ the module search path is carried out differently. The sequence of identifiers up to the last dot is used to find a "package"; the final identifier is then searched inside the package. A package is generally a subdirectory of a -directory on ``sys.path`` that has a file :file:`__init__.py`. [XXX Can't be -bothered to spell this out right now; see the URL -http://www.python.org/doc/essays/packages.html for more details, also about how -the module search works from inside a package.] +directory on ``sys.path`` that has a file :file:`__init__.py`. + +.. + [XXX Can't be + bothered to spell this out right now; see the URL + http://www.python.org/doc/essays/packages.html for more details, also about how + the module search works from inside a package.] .. index:: builtin: __import__ From python-checkins at python.org Fri Aug 1 22:31:19 2008 From: python-checkins at python.org (georg.brandl) Date: Fri, 1 Aug 2008 22:31:19 +0200 (CEST) Subject: [Python-checkins] r65380 - doctools/branches/0.4.x/tests/test_env.py Message-ID: <20080801203119.24FD31E4006@bag.python.org> Author: georg.brandl Date: Fri Aug 1 22:31:18 2008 New Revision: 65380 Log: Don't rely on mtimes being different for changed files. Modified: doctools/branches/0.4.x/tests/test_env.py Modified: doctools/branches/0.4.x/tests/test_env.py ============================================================================== --- doctools/branches/0.4.x/tests/test_env.py (original) +++ doctools/branches/0.4.x/tests/test_env.py Fri Aug 1 22:31:18 2008 @@ -65,10 +65,10 @@ assert set(latexbuilder.images.values()) == set(['img.pdf', 'img.png', 'img1.png']) def test_second_update(): - # delete, add and edit some files and update again + # delete, add and "edit" (change saved mtime) some files and update again + env.all_docs['contents'] = 0 root = path(app.srcdir) (root / 'images.txt').unlink() - (root / 'contents.txt').write_text('Changed file') (root / 'new.txt').write_text('New file\n========\n') it = env.update(app.config, app.srcdir, app.doctreedir, app) msg = it.next() From buildbot at python.org Fri Aug 1 22:50:38 2008 From: buildbot at python.org (buildbot at python.org) Date: Fri, 01 Aug 2008 20:50:38 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 trunk Message-ID: <20080801205038.793991E400D@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%20trunk/builds/86 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: jesse.noller BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 04:13:56 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 02:13:56 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 3.0 Message-ID: <20080802021356.466931E4003@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%203.0/builds/84 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: georg.brandl,jesse.noller BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 04:19:19 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 02:19:19 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080802021919.5B2A91E4003@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/88 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: georg.brandl,jesse.noller BUILD FAILED: failed compile sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 04:57:17 2008 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 2 Aug 2008 04:57:17 +0200 (CEST) Subject: [Python-checkins] r65382 - python/trunk/Doc/tutorial/controlflow.rst Message-ID: <20080802025717.5E4A81E4003@bag.python.org> Author: benjamin.peterson Date: Sat Aug 2 04:57:17 2008 New Revision: 65382 Log: fix indentation that caused logic bug Modified: python/trunk/Doc/tutorial/controlflow.rst Modified: python/trunk/Doc/tutorial/controlflow.rst ============================================================================== --- python/trunk/Doc/tutorial/controlflow.rst (original) +++ python/trunk/Doc/tutorial/controlflow.rst Sat Aug 2 04:57:17 2008 @@ -138,9 +138,9 @@ ... if n % x == 0: ... print n, 'equals', x, '*', n/x ... break - ... else: - ... # loop fell through without finding a factor - ... print n, 'is a prime number' + ... else: + ... # loop fell through without finding a factor + ... print n, 'is a prime number' ... 2 is a prime number 3 is a prime number From python-checkins at python.org Sat Aug 2 05:05:12 2008 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 2 Aug 2008 05:05:12 +0200 (CEST) Subject: [Python-checkins] r65383 - python/trunk/Doc/tutorial/controlflow.rst Message-ID: <20080802030512.CE0361E4003@bag.python.org> Author: benjamin.peterson Date: Sat Aug 2 05:05:11 2008 New Revision: 65383 Log: revert last revision; code was right Modified: python/trunk/Doc/tutorial/controlflow.rst Modified: python/trunk/Doc/tutorial/controlflow.rst ============================================================================== --- python/trunk/Doc/tutorial/controlflow.rst (original) +++ python/trunk/Doc/tutorial/controlflow.rst Sat Aug 2 05:05:11 2008 @@ -138,9 +138,9 @@ ... if n % x == 0: ... print n, 'equals', x, '*', n/x ... break - ... else: - ... # loop fell through without finding a factor - ... print n, 'is a prime number' + ... else: + ... # loop fell through without finding a factor + ... print n, 'is a prime number' ... 2 is a prime number 3 is a prime number From python-checkins at python.org Sat Aug 2 05:11:16 2008 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 2 Aug 2008 05:11:16 +0200 (CEST) Subject: [Python-checkins] r65385 - python/trunk/Modules/_multiprocessing/pipe_connection.c Message-ID: <20080802031116.6EDE71E4003@bag.python.org> Author: benjamin.peterson Date: Sat Aug 2 05:11:16 2008 New Revision: 65385 Log: fix compile error on Windows Modified: python/trunk/Modules/_multiprocessing/pipe_connection.c Modified: python/trunk/Modules/_multiprocessing/pipe_connection.c ============================================================================== --- python/trunk/Modules/_multiprocessing/pipe_connection.c (original) +++ python/trunk/Modules/_multiprocessing/pipe_connection.c Sat Aug 2 05:11:16 2008 @@ -68,7 +68,7 @@ memcpy(*newbuffer, buffer, length); Py_BEGIN_ALLOW_THREADS - ret = ReadFile(conn->handle, *newbuffer+length, left, &length, NULL) + ret = ReadFile(conn->handle, *newbuffer+length, left, &length, NULL); Py_END_ALLOW_THREADS if (ret) { assert(length == left); From python-checkins at python.org Sat Aug 2 05:13:46 2008 From: python-checkins at python.org (brett.cannon) Date: Sat, 2 Aug 2008 05:13:46 +0200 (CEST) Subject: [Python-checkins] r65386 - python/trunk/Lib/threading.py Message-ID: <20080802031346.C274C1E4003@bag.python.org> Author: brett.cannon Date: Sat Aug 2 05:13:46 2008 New Revision: 65386 Log: Remove a tuple unpacking in a parameter list to suppress the SyntaxWarning with -3. Modified: python/trunk/Lib/threading.py Modified: python/trunk/Lib/threading.py ============================================================================== --- python/trunk/Lib/threading.py (original) +++ python/trunk/Lib/threading.py Sat Aug 2 05:13:46 2008 @@ -151,7 +151,8 @@ # Internal methods used by condition variables - def _acquire_restore(self, (count, owner)): + def _acquire_restore(self, count_owner): + count, owner = count_owner self.__block.acquire() self.__count = count self.__owner = owner From python-checkins at python.org Sat Aug 2 05:15:20 2008 From: python-checkins at python.org (brett.cannon) Date: Sat, 2 Aug 2008 05:15:20 +0200 (CEST) Subject: [Python-checkins] r65387 - python/trunk/Lib/tokenize.py Message-ID: <20080802031520.ACCA31E4003@bag.python.org> Author: brett.cannon Date: Sat Aug 2 05:15:20 2008 New Revision: 65387 Log: Remove a tuple unpacking in a parameter list to remove a SyntaxWarning raised while running under -3. Modified: python/trunk/Lib/tokenize.py Modified: python/trunk/Lib/tokenize.py ============================================================================== --- python/trunk/Lib/tokenize.py (original) +++ python/trunk/Lib/tokenize.py Sat Aug 2 05:15:20 2008 @@ -146,7 +146,9 @@ class StopTokenizing(Exception): pass -def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing +def printtoken(type, token, srow_scol, erow_ecol, line): # for testing + srow, scol = srow_scol + erow, ecol = erow_ecol print "%d,%d-%d,%d:\t%s\t%s" % \ (srow, scol, erow, ecol, tok_name[type], repr(token)) From python-checkins at python.org Sat Aug 2 05:28:43 2008 From: python-checkins at python.org (brett.cannon) Date: Sat, 2 Aug 2008 05:28:43 +0200 (CEST) Subject: [Python-checkins] r65391 - python/trunk/Lib/bsddb/dbutils.py Message-ID: <20080802032843.5E5811E4003@bag.python.org> Author: brett.cannon Date: Sat Aug 2 05:28:42 2008 New Revision: 65391 Log: Remove a dict.has_key() use to silence a warning raised under -3. Modified: python/trunk/Lib/bsddb/dbutils.py Modified: python/trunk/Lib/bsddb/dbutils.py ============================================================================== --- python/trunk/Lib/bsddb/dbutils.py (original) +++ python/trunk/Lib/bsddb/dbutils.py Sat Aug 2 05:28:42 2008 @@ -61,7 +61,7 @@ """ sleeptime = _deadlock_MinSleepTime max_retries = _kwargs.get('max_retries', -1) - if _kwargs.has_key('max_retries'): + if 'max_retries' in _kwargs: del _kwargs['max_retries'] while True: try: From python-checkins at python.org Sat Aug 2 05:32:14 2008 From: python-checkins at python.org (brett.cannon) Date: Sat, 2 Aug 2008 05:32:14 +0200 (CEST) Subject: [Python-checkins] r65393 - python/trunk/Lib/asyncore.py Message-ID: <20080802033214.38DD31E4003@bag.python.org> Author: brett.cannon Date: Sat Aug 2 05:32:13 2008 New Revision: 65393 Log: Remove a dict.has_key() use to silence a warning when running under -3. Modified: python/trunk/Lib/asyncore.py Modified: python/trunk/Lib/asyncore.py ============================================================================== --- python/trunk/Lib/asyncore.py (original) +++ python/trunk/Lib/asyncore.py Sat Aug 2 05:32:13 2008 @@ -267,7 +267,7 @@ fd = self._fileno if map is None: map = self._map - if map.has_key(fd): + if fd in map: #self.log_info('closing channel %d:%s' % (fd, self)) del map[fd] self._fileno = None From buildbot at python.org Sat Aug 2 05:35:16 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 03:35:16 +0000 Subject: [Python-checkins] buildbot failure in x86 osx.5 3.0 Message-ID: <20080802033516.849E91E4003@bag.python.org> The Buildbot has detected a new failure of x86 osx.5 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20osx.5%203.0/builds/100 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-x86-osx5 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: benjamin.peterson BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_email test_mailbox make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 05:37:51 2008 From: python-checkins at python.org (brett.cannon) Date: Sat, 2 Aug 2008 05:37:51 +0200 (CEST) Subject: [Python-checkins] r65395 - python/trunk/Lib/ConfigParser.py Message-ID: <20080802033751.2FC231E4003@bag.python.org> Author: brett.cannon Date: Sat Aug 2 05:37:50 2008 New Revision: 65395 Log: Remove a __getitem__() removal on an exception to silence a warning triggered under -3. Modified: python/trunk/Lib/ConfigParser.py Modified: python/trunk/Lib/ConfigParser.py ============================================================================== --- python/trunk/Lib/ConfigParser.py (original) +++ python/trunk/Lib/ConfigParser.py Sat Aug 2 05:37:50 2008 @@ -588,7 +588,7 @@ value = value % vars except KeyError, e: raise InterpolationMissingOptionError( - option, section, rawval, e[0]) + option, section, rawval, e.args[0]) else: break if "%(" in value: From python-checkins at python.org Sat Aug 2 05:39:07 2008 From: python-checkins at python.org (collin.winter) Date: Sat, 2 Aug 2008 05:39:07 +0200 (CEST) Subject: [Python-checkins] r65397 - sandbox/trunk/2to3/lib2to3/fixes/fix_imports.py Message-ID: <20080802033907.4549D1E4003@bag.python.org> Author: collin.winter Date: Sat Aug 2 05:39:06 2008 New Revision: 65397 Log: Patch #3480 by Nick Edds. Dramatically simplifies the fix_imports pattern, resulting in a reduction of the test_all_fixers runtime from 122+ secs to 59 secs (a good predictor of 2to3 performance). Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_imports.py Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_imports.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_imports.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_imports.py Sat Aug 2 05:39:06 2008 @@ -61,24 +61,23 @@ def build_pattern(mapping=MAPPING): - bare = set() - for old_module, new_module in mapping.items(): - bare.add(old_module) - yield """import_name< 'import' (module=%r - | dotted_as_names< any* module=%r any* >) > - """ % (old_module, old_module) - yield """import_from< 'from' module_name=%r 'import' - ( any | import_as_name< any 'as' any > | - import_as_names< any* >) > - """ % old_module - yield """import_name< 'import' - dotted_as_name< module_name=%r 'as' any > > - """ % old_module - # Find usages of module members in code e.g. urllib.foo(bar) - yield """power< module_name=%r - trailer<'.' any > any* > - """ % old_module - yield """bare_name=%s""" % alternates(bare) + mod_list = ' | '.join(["module='" + key + "'" for key in mapping.keys()]) + mod_name_list = ' | '.join(["module_name='" + key + "'" for key in mapping.keys()]) + yield """import_name< 'import' ((%s) + | dotted_as_names< any* (%s) any* >) > + """ % (mod_list, mod_list) + yield """import_from< 'from' (%s) 'import' + ( any | import_as_name< any 'as' any > | + import_as_names< any* >) > + """ % mod_name_list + yield """import_name< 'import' + dotted_as_name< (%s) 'as' any > > + """ % mod_name_list + # Find usages of module members in code e.g. urllib.foo(bar) + yield """power< (%s) + trailer<'.' any > any* > + """ % mod_name_list + yield """bare_name=%s""" % alternates(mapping.keys()) class FixImports(fixer_base.BaseFix): PATTERN = "|".join(build_pattern()) From buildbot at python.org Sat Aug 2 05:48:23 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 03:48:23 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo trunk Message-ID: <20080802034823.E616D1E4003@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%20trunk/builds/4013 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-x86 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: benjamin.peterson,brett.cannon,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_pickletools make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 05:57:42 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 03:57:42 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo trunk Message-ID: <20080802035742.C23101E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/1255 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: benjamin.peterson,brett.cannon,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 06:41:23 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 04:41:23 +0000 Subject: [Python-checkins] buildbot failure in ia64 Ubuntu trunk Message-ID: <20080802044123.D9E521E4003@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%20Ubuntu%20trunk/builds/413 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ia64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: benjamin.peterson,brett.cannon,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_ssl make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 07:03:19 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 05:03:19 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080802050319.9171F1E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/870 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_smtplib make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 08:22:05 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 06:22:05 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080802062205.BA6681E4003@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1184 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: benjamin.peterson,brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_xmlrpc_net make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 08:43:37 2008 From: python-checkins at python.org (martin.v.loewis) Date: Sat, 2 Aug 2008 08:43:37 +0200 (CEST) Subject: [Python-checkins] r65398 - tracker/roundup-src/roundup/cgi/client.py Message-ID: <20080802064337.546C31E4003@bag.python.org> Author: martin.v.loewis Date: Sat Aug 2 08:43:37 2008 New Revision: 65398 Log: Don't pass huge itemids into the backend. Modified: tracker/roundup-src/roundup/cgi/client.py Modified: tracker/roundup-src/roundup/cgi/client.py ============================================================================== --- tracker/roundup-src/roundup/cgi/client.py (original) +++ tracker/roundup-src/roundup/cgi/client.py Sat Aug 2 08:43:37 2008 @@ -706,6 +706,10 @@ klass = self.db.getclass(self.classname) except KeyError: raise NotFound, '%s/%s'%(self.classname, self.nodeid) + if long(self.nodeid) > 2**31: + # Postgres will complain with a ProgrammingError + # if we try to pass in numbers that are too large + raise NotFound, '%s/%s'%(self.classname, self.nodeid) if not klass.hasnode(self.nodeid): raise NotFound, '%s/%s'%(self.classname, self.nodeid) # with a designator, we default to item view From buildbot at python.org Sat Aug 2 09:02:52 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 07:02:52 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian trunk Message-ID: <20080802070252.3CCFA1E4003@bag.python.org> The Buildbot has detected a new failure of S-390 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%20trunk/builds/929 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 09:20:25 2008 From: python-checkins at python.org (martin.v.loewis) Date: Sat, 2 Aug 2008 09:20:25 +0200 (CEST) Subject: [Python-checkins] r65399 - in python/trunk: Lib/lib-tk/Tkinter.py Misc/NEWS Message-ID: <20080802072025.8CFED1E4003@bag.python.org> Author: martin.v.loewis Date: Sat Aug 2 09:20:25 2008 New Revision: 65399 Log: Issue #799428: Fix Tkinter.Misc._nametowidget to unwrap Tcl command objects. Modified: python/trunk/Lib/lib-tk/Tkinter.py python/trunk/Misc/NEWS Modified: python/trunk/Lib/lib-tk/Tkinter.py ============================================================================== --- python/trunk/Lib/lib-tk/Tkinter.py (original) +++ python/trunk/Lib/lib-tk/Tkinter.py Sat Aug 2 09:20:25 2008 @@ -1066,18 +1066,18 @@ def nametowidget(self, name): """Return the Tkinter instance of a widget identified by its Tcl name NAME.""" + name = str(name).split('.') w = self - if name[0] == '.': + + if not name[0]: w = w._root() name = name[1:] - while name: - i = name.find('.') - if i >= 0: - name, tail = name[:i], name[i+1:] - else: - tail = '' - w = w.children[name] - name = tail + + for n in name: + if not n: + break + w = w.children[n] + return w _nametowidget = nametowidget def _register(self, func, subst=None, needcleanup=1): Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Aug 2 09:20:25 2008 @@ -139,6 +139,8 @@ Library ------- +- Issue #799428: Fix Tkinter.Misc._nametowidget to unwrap Tcl command objects. + - Issue #3395: fix reference in test_multiprocessing to old debugInfo method - Issue #3312: Fix two crashes in sqlite3. From python-checkins at python.org Sat Aug 2 09:21:06 2008 From: python-checkins at python.org (martin.v.loewis) Date: Sat, 2 Aug 2008 09:21:06 +0200 (CEST) Subject: [Python-checkins] r65400 - in python/branches/release25-maint: Lib/lib-tk/Tkinter.py Misc/NEWS Message-ID: <20080802072106.7D97D1E4003@bag.python.org> Author: martin.v.loewis Date: Sat Aug 2 09:21:06 2008 New Revision: 65400 Log: Issue #799428: Fix Tkinter.Misc._nametowidget to unwrap Tcl command objects. Backport of r65399. Modified: python/branches/release25-maint/Lib/lib-tk/Tkinter.py python/branches/release25-maint/Misc/NEWS Modified: python/branches/release25-maint/Lib/lib-tk/Tkinter.py ============================================================================== --- python/branches/release25-maint/Lib/lib-tk/Tkinter.py (original) +++ python/branches/release25-maint/Lib/lib-tk/Tkinter.py Sat Aug 2 09:21:06 2008 @@ -1072,18 +1072,18 @@ def nametowidget(self, name): """Return the Tkinter instance of a widget identified by its Tcl name NAME.""" + name = str(name).split('.') w = self - if name[0] == '.': + + if not name[0]: w = w._root() name = name[1:] - while name: - i = name.find('.') - if i >= 0: - name, tail = name[:i], name[i+1:] - else: - tail = '' - w = w.children[name] - name = tail + + for n in name: + if not n: + break + w = w.children[n] + return w _nametowidget = nametowidget def _register(self, func, subst=None, needcleanup=1): Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Sat Aug 2 09:21:06 2008 @@ -74,6 +74,8 @@ Library ------- +- Issue #799428: Fix Tkinter.Misc._nametowidget to unwrap Tcl command objects. + - Issue #3339: dummy_thread.acquire() could return None which is not a valid return value. From buildbot at python.org Sat Aug 2 10:18:50 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 08:18:50 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 trunk Message-ID: <20080802081851.187651E4003@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%20trunk/builds/88 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 11:38:13 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 09:38:13 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080802093813.819FF1E4003@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/92 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: martin.v.loewis BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "", line 1, in File "C:\buildbot\3.0.heller-windows-amd64\build\lib\multiprocessing\forking.py", line 297, in main self = load(from_parent) File "C:\buildbot\3.0.heller-windows-amd64\build\lib\pickle.py", line 1325, in load return Unpickler(file, encoding=encoding, errors=errors).load() EOFError 4 tests failed: test_io test_largefile test_multiprocessing test_sys ====================================================================== ERROR: test_large_file_ops (test.test_io.IOTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_io.py", line 213, in test_large_file_ops self.large_file_ops(f) File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_io.py", line 139, in large_file_ops self.assertEqual(f.write(b"xxx"), 3) IOError: [Errno 28] No space left on device ====================================================================== ERROR: test_seek (test.test_largefile.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_largefile.py", line 42, in test_seek f.flush() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 28] No space left on device ====================================================================== FAIL: test_osstat (test.test_largefile.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_largefile.py", line 50, in test_osstat self.assertEqual(os.stat(TESTFN)[stat.ST_SIZE], size+1) AssertionError: 1 != 2500000001 ====================================================================== FAIL: test_seek_read (test.test_largefile.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_largefile.py", line 72, in test_seek_read self.assertEqual(f.tell(), size + 1 + 0) AssertionError: 1 != 2500000001 ====================================================================== FAIL: test_lseek (test.test_largefile.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_largefile.py", line 93, in test_lseek self.assertEqual(os.lseek(f.fileno(), 0, 2), size+1+0) AssertionError: 1 != 2500000001 ====================================================================== FAIL: test_truncate (test.test_largefile.TestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_largefile.py", line 110, in test_truncate self.assertEqual(f.tell(), size+1) AssertionError: 1 != 2500000001 Traceback (most recent call last): File "../lib/test/regrtest.py", line 603, in runtest_inner indirect_test() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_multiprocessing.py", line 1763, in test_main ManagerMixin.manager.start() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\multiprocessing\managers.py", line 499, in start self._process.start() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\multiprocessing\process.py", line 104, in start self._popen = Popen(self) File "C:\buildbot\3.0.heller-windows-amd64\build\lib\multiprocessing\forking.py", line 194, in __init__ dump(process_obj, to_child, HIGHEST_PROTOCOL) File "C:\buildbot\3.0.heller-windows-amd64\build\lib\pickle.py", line 1315, in dump Pickler(file, protocol).dump(obj) _pickle.PicklingError: Can't pickle : attribute lookup builtins.method failed ====================================================================== FAIL: test_objecttypes (test.test_sys.SizeofTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_sys.py", line 577, in test_objecttypes check(range(66000), size(h + '3l')) File "C:\buildbot\3.0.heller-windows-amd64\build\lib\test\test_sys.py", line 393, in check_sizeof self.assertEqual(result, size, msg) AssertionError: wrong size for : got 56, expected 48 sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 11:52:58 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 09:52:58 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian 3.0 Message-ID: <20080802095259.489991E4003@bag.python.org> The Buildbot has detected a new failure of S-390 Debian 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%203.0/builds/676 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: martin.v.loewis BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 13:26:40 2008 From: python-checkins at python.org (lars.gustaebel) Date: Sat, 2 Aug 2008 13:26:40 +0200 (CEST) Subject: [Python-checkins] r65402 - in python/trunk: Doc/library/tarfile.rst Lib/tarfile.py Misc/NEWS Message-ID: <20080802112640.02B401E400E@bag.python.org> Author: lars.gustaebel Date: Sat Aug 2 13:26:39 2008 New Revision: 65402 Log: Issue #3039: Fix TarFileCompat.writestr() which always raised an AttributeError since __slots__ were added to zipfile.ZipInfo in r46967 two years ago. Add a warning about the removal of TarFileCompat in Python 3.0. Modified: python/trunk/Doc/library/tarfile.rst python/trunk/Lib/tarfile.py python/trunk/Misc/NEWS Modified: python/trunk/Doc/library/tarfile.rst ============================================================================== --- python/trunk/Doc/library/tarfile.rst (original) +++ python/trunk/Doc/library/tarfile.rst Sat Aug 2 13:26:39 2008 @@ -140,6 +140,10 @@ Constant for a :mod:`gzip` compressed tar archive. + .. deprecated:: 2.6 + The :class:`TarFileCompat` class has been deprecated for removal in Python 3.0. + + .. exception:: TarError Base class for all :mod:`tarfile` exceptions. Modified: python/trunk/Lib/tarfile.py ============================================================================== --- python/trunk/Lib/tarfile.py (original) +++ python/trunk/Lib/tarfile.py Sat Aug 2 13:26:39 2008 @@ -2468,6 +2468,9 @@ ZipFile class. """ def __init__(self, file, mode="r", compression=TAR_PLAIN): + from warnings import warnpy3k + warnpy3k("the TarFileCompat class has been removed in Python 3.0", + stacklevel=2) if compression == TAR_PLAIN: self.tarfile = TarFile.taropen(file, mode) elif compression == TAR_GZIPPED: @@ -2501,10 +2504,10 @@ except ImportError: from StringIO import StringIO import calendar - zinfo.name = zinfo.filename - zinfo.size = zinfo.file_size - zinfo.mtime = calendar.timegm(zinfo.date_time) - self.tarfile.addfile(zinfo, StringIO(bytes)) + tinfo = TarInfo(zinfo.filename) + tinfo.size = len(bytes) + tinfo.mtime = calendar.timegm(zinfo.date_time) + self.tarfile.addfile(tinfo, StringIO(bytes)) def close(self): self.tarfile.close() #class TarFileCompat Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Sat Aug 2 13:26:39 2008 @@ -38,6 +38,9 @@ Library ------- +- Issue #3039: Fix tarfile.TarFileCompat.writestr() which always + raised an AttributeError. + - Issue #2523: Fix quadratic behaviour when read()ing a binary file without asking for a specific length. From buildbot at python.org Sat Aug 2 14:15:13 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 12:15:13 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080802121513.4A3CB1E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/872 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: lars.gustaebel BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_io test_smtplib ====================================================================== ERROR: testBasicIO (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 823, in testBasicIO self.assertEquals(f.write("abc"), 3) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1459, in write b = encoder.encode(s) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 22, in encode return codecs.ascii_encode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_encode' ====================================================================== ERROR: testEncodingErrorsReading (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 661, in testEncodingErrorsReading self.assertRaises(UnicodeError, t.read) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/unittest.py", line 311, in failUnlessRaises callableObj(*args, **kwargs) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1692, in read decoder.decode(self.buffer.read(), final=True)) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1267, in decode output = self.decoder.decode(input, final=final) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_decode' ====================================================================== ERROR: testEncodingErrorsWriting (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 679, in testEncodingErrorsWriting self.assertRaises(UnicodeError, t.write, "\xff") File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/unittest.py", line 311, in failUnlessRaises callableObj(*args, **kwargs) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1459, in write b = encoder.encode(s) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 22, in encode return codecs.ascii_encode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_encode' ====================================================================== ERROR: testNewlinesInput (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 784, in testNewlinesInput self.assertEquals(txt.readlines(), expected) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 536, in readlines return list(self) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1707, in __next__ line = self.readline() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1781, in readline while self._read_chunk(): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1530, in _read_chunk self._set_decoded_chars(self._decoder.decode(input_chunk, eof)) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1267, in decode output = self.decoder.decode(input, final=final) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_decode' ====================================================================== ERROR: testNewlinesOutput (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 809, in testNewlinesOutput txt.write(data) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1459, in write b = encoder.encode(s) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 22, in encode return codecs.ascii_encode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_encode' ====================================================================== ERROR: test_issue1395_1 (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 1045, in test_issue1395_1 c = txt.read(1) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1701, in read eof = not self._read_chunk() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1530, in _read_chunk self._set_decoded_chars(self._decoder.decode(input_chunk, eof)) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1267, in decode output = self.decoder.decode(input, final=final) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_decode' ====================================================================== ERROR: test_issue1395_2 (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 1057, in test_issue1395_2 c = txt.read(4) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1701, in read eof = not self._read_chunk() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1530, in _read_chunk self._set_decoded_chars(self._decoder.decode(input_chunk, eof)) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1267, in decode output = self.decoder.decode(input, final=final) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_decode' ====================================================================== ERROR: test_issue1395_3 (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 1067, in test_issue1395_3 reads = txt.read(4) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1701, in read eof = not self._read_chunk() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1530, in _read_chunk self._set_decoded_chars(self._decoder.decode(input_chunk, eof)) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1267, in decode output = self.decoder.decode(input, final=final) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_decode' ====================================================================== ERROR: test_issue1395_4 (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 1078, in test_issue1395_4 reads = txt.read(4) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1701, in read eof = not self._read_chunk() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1530, in _read_chunk self._set_decoded_chars(self._decoder.decode(input_chunk, eof)) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1267, in decode output = self.decoder.decode(input, final=final) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_decode' ====================================================================== ERROR: test_issue1395_5 (test.test_io.TextIOWrapperTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/test/test_io.py", line 1086, in test_issue1395_5 reads = txt.read(4) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1701, in read eof = not self._read_chunk() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1530, in _read_chunk self._set_decoded_chars(self._decoder.decode(input_chunk, eof)) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1267, in decode output = self.decoder.decode(input, final=final) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] AttributeError: 'NoneType' object has no attribute 'ascii_decode' make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 15:19:29 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 15:19:29 +0200 (CEST) Subject: [Python-checkins] r65405 - sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py Message-ID: <20080802131929.CCAD01E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 15:19:29 2008 New Revision: 65405 Log: Removed unused classes, erradicated the dependency of self.text here Modified: sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py Sat Aug 2 15:19:29 2008 @@ -4,6 +4,11 @@ import IOBinding from EditorWindow import EditorWindow +def _callback(func, *myargs): + def w(*args): + return func(*(args + myargs)) + return w + class OutputWindow(EditorWindow): """An editor window that can serve as an output file. @@ -14,7 +19,14 @@ def __init__(self, *args): EditorWindow.__init__(self, *args) - self.text.bind("<>", self.goto_file_line) + self.top.bind("<>", self.__configure_new_tab) + # configure the tab created in EditorWindow.__init__ + self.__configure_new_tab() + + def __configure_new_tab(self, event=None): + page = self.text_notebook.last_page().editpage + page.text.bind("<>", _callback(self.goto_file_line, + page.text)) # Customize EditorWindow @@ -34,7 +46,8 @@ # Act as output file - def write(self, s, tags=(), mark="insert"): + def write(self, s, tags=(), mark="insert", text=None): + assert text is not None # Tk assumes that byte strings are Latin-1; # we assume that they are in the locale's encoding if isinstance(s, str): @@ -43,12 +56,12 @@ except UnicodeError: # some other encoding; let Tcl deal with it pass - self.text.insert(mark, s, tags) - self.text.see(mark) - self.text.update() + text.insert(mark, s, tags) + text.see(mark) + text.update() def writelines(self, l): - map(self.write, l) + map(self.write, l, text=self.current_page.text) def flush(self): pass @@ -67,28 +80,26 @@ file_line_progs = None - def goto_file_line(self, event=None): + def goto_file_line(self, event, text): if self.file_line_progs is None: l = [] for pat in self.file_line_pats: l.append(re.compile(pat, re.IGNORECASE)) self.file_line_progs = l - # x, y = self.event.x, self.event.y - # self.text.mark_set("insert", "@%d,%d" % (x, y)) - line = self.text.get("insert linestart", "insert lineend") + + line = text.get("insert linestart", "insert lineend") result = self._file_line_helper(line) if not result: # Try the previous line. This is handy e.g. in tracebacks, # where you tend to right-click on the displayed source line - line = self.text.get("insert -1line linestart", - "insert -1line lineend") + line = text.get("insert -1line linestart", "insert -1line ineend") result = self._file_line_helper(line) if not result: tkMessageBox.showerror( "No special line", "The line you point at doesn't look like " "a valid file name followed by a line number.", - master=self.text) + master=text) return filename, lineno = result edit = self.flist.open(filename) @@ -111,47 +122,3 @@ return filename, int(lineno) except TypeError: return None - -# These classes are currently not used but might come in handy - -class OnDemandOutputWindow: - - tagdefs = { - # XXX Should use IdlePrefs.ColorPrefs - "stdout": {"foreground": "blue"}, - "stderr": {"foreground": "#007700"}, - } - - def __init__(self, flist): - self.flist = flist - self.owin = None - - def write(self, s, tags, mark): - if not self.owin: - self.setup() - self.owin.write(s, tags, mark) - - def setup(self): - self.owin = owin = OutputWindow(self.flist) - text = owin.text - for tag, cnf in self.tagdefs.items(): - if cnf: - text.tag_configure(tag, **cnf) - text.tag_raise('sel') - self.write = self.owin.write - -#class PseudoFile: -# -# def __init__(self, owin, tags, mark="end"): -# self.owin = owin -# self.tags = tags -# self.mark = mark - -# def write(self, s): -# self.owin.write(s, self.tags, self.mark) - -# def writelines(self, l): -# map(self.write, l) - -# def flush(self): -# pass From python-checkins at python.org Sat Aug 2 15:20:47 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 15:20:47 +0200 (CEST) Subject: [Python-checkins] r65406 - sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Message-ID: <20080802132047.488971E401C@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 15:20:46 2008 New Revision: 65406 Log: Renamed self.text to self.wtext, renamed _configure_new_tab to __configure_new_tab to not cause problems Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Sat Aug 2 15:20:46 2008 @@ -119,15 +119,14 @@ ("Clear Breakpoint", "<>")] def __init__(self, *args): - #self.breakpoints = [] EditorWindow.__init__(self, *args) self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(), 'breakpoints.lst') - self.top.bind('<>', self._configure_new_tab) - self._configure_new_tab() + self.top.bind('<>', self.__configure_new_tab) + self.__configure_new_tab() - def _configure_new_tab(self, event=None): + def __configure_new_tab(self, event=None): page = self.text_notebook.last_page().editpage page.breakpoints = [] text = page.text @@ -849,9 +848,9 @@ self.indentwidth = 8 self.context_use_ps1 = True # - self.text = None + self.wtext = None self.textpage = None - self._configure_new_tab() + self.__configure_new_tab() self.save_stdout = sys.stdout self.save_stderr = sys.stderr @@ -870,10 +869,10 @@ if TTK: self.set_theme(Style(self.root)) - def _configure_new_tab(self, event=None): + def __configure_new_tab(self): # select the last page (the one added) page = self.text_notebook.last_page().editpage - self.text = text = page.text + self.wtext = text = page.text self.textpage = page text.configure(wrap="char") @@ -1165,15 +1164,15 @@ s = re.sub(r'^\s*\n', '' , s) s = re.sub(r'\n\s*$', '', s) lines = s.split('\n') - self.text.undo_block_start() + self.wtext.undo_block_start() try: - self.text.tag_remove("sel", "1.0", "end") - self.text.mark_set("insert", "end-1c") - prefix = self.text.get("insert linestart", "insert") + self.wtext.tag_remove("sel", "1.0", "end") + self.wtext.mark_set("insert", "end-1c") + prefix = self.wtext.get("insert linestart", "insert") if prefix.rstrip().endswith(':'): self.newline_and_indent_event(event) - prefix = self.text.get("insert linestart", "insert") - self.text.insert("insert", lines[0].strip()) + prefix = self.wtext.get("insert linestart", "insert") + self.wtext.insert("insert", lines[0].strip()) if len(lines) > 1: orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) @@ -1181,10 +1180,10 @@ if line.startswith(orig_base_indent): # replace orig base indentation with new indentation line = new_base_indent + line[len(orig_base_indent):] - self.text.insert('insert', '\n'+line.rstrip()) + self.wtext.insert('insert', '\n'+line.rstrip()) finally: - self.text.see("insert") - self.text.undo_block_stop() + self.wtext.see("insert") + self.wtext.undo_block_stop() def _runit(self, text): line = text.get("iomark", "end-1c") @@ -1235,20 +1234,20 @@ curr_page.io.reset_undo() def resetoutput(self): - source = self.text.get("iomark", "end-1c") + source = self.wtext.get("iomark", "end-1c") if self.textpage.history: self.textpage.history.history_store(source) - if self.text.get("end-2c") != "\n": - self.text.insert("end-1c", "\n") - self.text.mark_set("iomark", "end-1c") + if self.wtext.get("end-2c") != "\n": + self.wtext.insert("end-1c", "\n") + self.wtext.mark_set("iomark", "end-1c") self.set_line_and_column() sys.stdout.softspace = 0 def write(self, s, tags=()): try: - self.text.mark_gravity("iomark", "right") - OutputWindow.write(self, s, tags, "iomark") - self.text.mark_gravity("iomark", "left") + self.wtext.mark_gravity("iomark", "right") + OutputWindow.write(self, s, tags, "iomark", self.wtext) + self.wtext.mark_gravity("iomark", "left") except: pass if self.canceled: From python-checkins at python.org Sat Aug 2 15:21:38 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 15:21:38 +0200 (CEST) Subject: [Python-checkins] r65407 - sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Message-ID: <20080802132138.3B6D81E4007@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 15:21:37 2008 New Revision: 65407 Log: Removed more dependencies of self.text. Now when changing tabs, the text widget is focused Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Sat Aug 2 15:21:37 2008 @@ -325,27 +325,36 @@ for page in self.text_notebook.pages.itervalues(): page.editpage.reset_colorizer() - def ResetFont(self): # XXX depends on self.text + def ResetFont(self): "Update the text widgets' font if it is changed" # Called from configDialog.py fontWeight = 'normal' if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'): fontWeight = 'bold' - self.text.config(font=(idleConf.GetOption('main', 'EditorPage', 'font'), + + for page in self.text_notebook.pages.itervalues(): + text = page.editpage.text + text.config(font=(idleConf.GetOption('main', 'EditorPage', 'font'), idleConf.GetOption('main', 'EditorPage', 'font-size'), fontWeight)) - def RemoveKeybindings(self): # XXX depends on self.text + def RemoveKeybindings(self): "Remove the keybindings before they are changed." # Called from configDialog.py Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() - for event, keylist in keydefs.items(): - self.text.event_delete(event, *keylist) + + for page in self.text_notebook.pages.itervalues(): + text = page.editpage.text + for event, keylist in keydefs.items(): + text.event_delete(event, *keylist) + for extensionName in self.get_standard_extension_names(): xkeydefs = idleConf.GetExtensionBindings(extensionName) if xkeydefs: - for event, keylist in xkeydefs.items(): - self.text.event_delete(event, *keylist) + for page in self.text_notebook.pages.itervalues(): + text = page.editpage.text + for event, keylist in xkeydefs.items(): + text.event_delete(event, *keylist) def ApplyKeybindings(self): "Update the keybindings after they are changed" @@ -509,7 +518,7 @@ def get_standard_extension_names(self): return idleConf.GetExtensions(editor_only=True) - def load_extension(self, name): # XXX depends on self.text + def load_extension(self, name): try: mod = __import__(name, globals(), locals(), []) except ImportError: @@ -530,8 +539,10 @@ while methodname[-1:] == '>': methodname = methodname[:-1] methodname = methodname + "_event" - if hasattr(ins, methodname): - self.text.bind(vevent, getattr(ins, methodname)) + for page in self.text_notebook.pages.itervalues(): + text = page.editpage.text + if hasattr(ins, methodname): + text.bind(vevent, getattr(ins, methodname)) def apply_bindings(self, keydefs=None): if keydefs is None: @@ -615,6 +626,8 @@ # Private methods def _update_controls(self, event): + print self.short_title(), "<<" + self.current_page.text.focus_set() self.io = self.current_page.io self.set_line_and_column() From python-checkins at python.org Sat Aug 2 15:34:41 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 15:34:41 +0200 (CEST) Subject: [Python-checkins] r65408 - sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py Message-ID: <20080802133441.829A91E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 15:34:41 2008 New Revision: 65408 Log: Added a reference to the editor window, looks more correct now Modified: sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py Sat Aug 2 15:34:41 2008 @@ -145,13 +145,13 @@ class IOBinding: - # XXX will need to change this too + filename_change_hook = None + filename = None + dirname = None - #def __init__(self, editwin): def __init__(self, editpage): - #self.editwin = editwin - #self.text = editwin.text self.editpage = editpage + self.editwin = editpage.editwin self.text = editpage.text self.__id_open = self.text.bind("<>", self.open) self.__id_save = self.text.bind("<>", self.save) @@ -170,31 +170,23 @@ self.text.unbind("<>", self.__id_savecopy) self.text.unbind("<>", self.__id_print) # Break cycles - #self.editwin = None - self.editpage = None self.text = None + self.editpage = None + self.editwin = None self.filename_change_hook = None def get_saved(self): - #return self.editwin.get_saved() return self.editpage.get_saved() def set_saved(self, flag): - #self.editwin.set_saved(flag) self.editpage.set_saved(flag) def reset_undo(self): - #self.editwin.reset_undo() self.editpage.reset_undo() - filename_change_hook = None - def set_filename_change_hook(self, hook): self.filename_change_hook = hook - filename = None - dirname = None - def set_filename(self, filename): if filename and os.path.isdir(filename): self.filename = None @@ -211,7 +203,7 @@ if not editFile: filename = self.askopenfile() else: - filename=editFile + filename = editFile if filename: # If the current window has no filename and hasn't been # modified, we replace its contents (no loss). Otherwise @@ -352,8 +344,8 @@ if self.writefile(self.filename): self.set_saved(1) try: - self.editpage.editwin.store_file_breaks(self.editpage) - except AttributeError: # may be a PyShell + self.editwin.store_file_breaks(self.editpage) + except AttributeError: # may not be a PyShell pass self.text.focus_set() return "break" @@ -365,7 +357,7 @@ self.set_filename(filename) self.set_saved(1) try: - self.editpage.editwin.store_file_breaks(self.editpage) + self.editwin.store_file_breaks(self.editpage) except AttributeError: pass self.text.focus_set() @@ -392,8 +384,7 @@ f.close() return True except IOError, msg: - tkMessageBox.showerror("I/O Error", str(msg), - master=self.text) + tkMessageBox.showerror("I/O Error", str(msg), master=self.text) return False def encode(self, chars): @@ -455,8 +446,7 @@ enc = "utf-8" if not ask_user: return chars - #dialog = EncodingMessage(self.editwin.top, enc) - dialog = EncodingMessage(self.editpage.editwin.top, enc) + dialog = EncodingMessage(self.editwin.top, enc) dialog.go() if dialog.num == 1: # User asked us to edit the file @@ -571,8 +561,7 @@ def updaterecentfileslist(self,filename): "Update recent file list on all editor windows" - #self.editwin.update_recent_files_list(filename) - self.editpage.editwin.update_recent_files_list(filename) + self.editwin.update_recent_files_list(filename) def test(): from Tkinter import Tk, Text From buildbot at python.org Sat Aug 2 16:05:59 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 14:05:59 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080802140559.A8E9A1E4007@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1186 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: lars.gustaebel BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 16:11:53 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 16:11:53 +0200 (CEST) Subject: [Python-checkins] r65409 - in sandbox/trunk/ttk-gsoc/src/idlelib: ClassBrowser.py PathBrowser.py Message-ID: <20080802141153.96EF81E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 16:11:53 2008 New Revision: 65409 Log: Removed eminent cyclic module reference (EditorWindow -> editorpage -> PathBrowser -> ClassBrowser -> PyShell -> FileList -> EditorWindow) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ClassBrowser.py sandbox/trunk/ttk-gsoc/src/idlelib/PathBrowser.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ClassBrowser.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/ClassBrowser.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/ClassBrowser.py Sat Aug 2 16:11:53 2008 @@ -14,7 +14,6 @@ import sys import pyclbr -import PyShell from WindowList import ListedToplevel from TreeWidget import TreeNode, TreeItem, ScrolledCanvas from configHandler import idleConf @@ -26,6 +25,7 @@ # XXX the code here is bogus! self.name = name self.file = os.path.join(path[0], self.name + ".py") + self.flist = None self.init(flist) def close(self, event=None): @@ -57,12 +57,13 @@ self.top.wm_iconname("Class Browser") def rootnode(self): - return ModuleBrowserTreeItem(self.file) + return ModuleBrowserTreeItem(self.file, self.flist) class ModuleBrowserTreeItem(TreeItem): - def __init__(self, file): + def __init__(self, file, flist): self.file = file + self.flist = flist def GetText(self): return os.path.basename(self.file) @@ -73,7 +74,8 @@ def GetSubList(self): sublist = [] for name in self.listclasses(): - item = ClassBrowserTreeItem(name, self.classes, self.file) + item = ClassBrowserTreeItem(name, self.classes, self.file, + self.flist) sublist.append(item) return sublist @@ -82,7 +84,7 @@ return if not os.path.exists(self.file): return - PyShell.flist.open(self.file) + self.flist.open(self.file) def IsExpandable(self): return os.path.normcase(self.file[-3:]) == ".py" @@ -122,10 +124,11 @@ class ClassBrowserTreeItem(TreeItem): - def __init__(self, name, classes, file): + def __init__(self, name, classes, file, flist): self.name = name self.classes = classes self.file = file + self.flist = flist try: self.cl = self.classes[self.name] except (IndexError, KeyError): @@ -163,7 +166,7 @@ def OnDoubleClick(self): if not os.path.exists(self.file): return - edit = PyShell.flist.open(self.file) + edit = self.flist.open(self.file) if hasattr(self.cl, 'lineno'): lineno = self.cl.lineno edit.gotoline(lineno) @@ -199,10 +202,11 @@ def OnDoubleClick(self): if not os.path.exists(self.file): return - edit = PyShell.flist.open(self.file) + edit = self.flist.open(self.file) edit.gotoline(self.cl.methods[self.name]) def main(): + import PyShell try: file = __file__ except NameError: Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PathBrowser.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PathBrowser.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PathBrowser.py Sat Aug 2 16:11:53 2008 @@ -15,25 +15,29 @@ self.top.wm_iconname("Path Browser") def rootnode(self): - return PathBrowserTreeItem() + return PathBrowserTreeItem(self.flist) class PathBrowserTreeItem(TreeItem): + def __init__(self, flist): + self.flist = flist + def GetText(self): return "sys.path" def GetSubList(self): sublist = [] for dir in sys.path: - item = DirBrowserTreeItem(dir) + item = DirBrowserTreeItem(dir, flist=self.flist) sublist.append(item) return sublist class DirBrowserTreeItem(TreeItem): - def __init__(self, dir, packages=[]): + def __init__(self, dir, packages=[], flist=None): self.dir = dir self.packages = packages + self.flist = flist def GetText(self): if not self.packages: @@ -55,10 +59,11 @@ packages.sort() sublist = [] for nn, name, file in packages: - item = DirBrowserTreeItem(file, self.packages + [name]) + item = DirBrowserTreeItem(file, self.packages + [name], self.flist) sublist.append(item) for nn, name in self.listmodules(names): - item = ModuleBrowserTreeItem(os.path.join(self.dir, name)) + item = ModuleBrowserTreeItem(os.path.join(self.dir, name), + self.flist) sublist.append(item) return sublist From python-checkins at python.org Sat Aug 2 16:12:38 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 16:12:38 +0200 (CEST) Subject: [Python-checkins] r65410 - in sandbox/trunk/ttk-gsoc/src/idlelib: OutputWindow.py PyShell.py utils.py Message-ID: <20080802141238.2D2881E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 16:12:37 2008 New Revision: 65410 Log: Added a utils.callback to call a callback with arbitrary arguments Added: sandbox/trunk/ttk-gsoc/src/idlelib/utils.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/OutputWindow.py Sat Aug 2 16:12:37 2008 @@ -1,14 +1,10 @@ import re import tkMessageBox +import utils import IOBinding from EditorWindow import EditorWindow -def _callback(func, *myargs): - def w(*args): - return func(*(args + myargs)) - return w - class OutputWindow(EditorWindow): """An editor window that can serve as an output file. @@ -25,7 +21,7 @@ def __configure_new_tab(self, event=None): page = self.text_notebook.last_page().editpage - page.text.bind("<>", _callback(self.goto_file_line, + page.text.bind("<>", utils.callback(self.goto_file_line, page.text)) # Customize EditorWindow Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Sat Aug 2 16:12:37 2008 @@ -35,6 +35,7 @@ idleConf.SaveUserCfgFiles() import rpc +import utils import idlever import Debugger import RemoteDebugger @@ -83,11 +84,6 @@ return s warnings.formatwarning = idle_formatwarning -def _callback(func, *myargs): - def w(*args): - return func(*(args + myargs)) - return w - def extended_linecache_checkcache(filename=None, orig_checkcache=linecache.checkcache): """Extend linecache.checkcache to preserve the entries @@ -132,9 +128,9 @@ text = page.text text.bind("<>", - _callback(self._set_breakpoint_here, text, page)) + utils.callback(self._set_breakpoint_here, text, page)) text.bind("<>", - _callback(self._clear_breakpoint_here, text, page)) + utils.callback(self._clear_breakpoint_here, text, page)) text.bind("<>", self.flist.open_shell) # whenever a file is changed, restore breakpoints @@ -877,19 +873,19 @@ text.configure(wrap="char") text.bind("<>", - _callback(self._enter_callback, text)) + utils.callback(self._enter_callback, text)) text.bind("<>", - _callback(self._linefeed_callback, text)) + utils.callback(self._linefeed_callback, text)) text.bind("<>", - _callback(self._cancel_callback, text)) + utils.callback(self._cancel_callback, text)) text.bind("<>", - _callback(self._eof_callback, text)) + utils.callback(self._eof_callback, text)) text.bind("<>", self._toggle_debugger) text.bind("<>", self._toggle_jit_stack_viewer) text.bind("<>", self.open_stack_viewer) if use_subprocess: text.bind("<>", - _callback(self._view_restart_mark, text)) + utils.callback(self._view_restart_mark, text)) text.bind("<>", self.restart_shell) page.history = self.History(text) Added: sandbox/trunk/ttk-gsoc/src/idlelib/utils.py ============================================================================== --- (empty file) +++ sandbox/trunk/ttk-gsoc/src/idlelib/utils.py Sat Aug 2 16:12:37 2008 @@ -0,0 +1,4 @@ +def callback(func, *myargs): + def w(*args): + return func(*(args + myargs)) + return w From python-checkins at python.org Sat Aug 2 16:13:42 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 16:13:42 +0200 (CEST) Subject: [Python-checkins] r65411 - in sandbox/trunk/ttk-gsoc/src/idlelib: EditorWindow.py editorpage.py Message-ID: <20080802141342.1FFAD1E4006@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 16:13:41 2008 New Revision: 65411 Log: Moved more code to editorpage to reduce dependence on self.text of EditorWindow Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Sat Aug 2 16:13:41 2008 @@ -11,8 +11,6 @@ import macosxSupport import Bindings import WindowList -import PathBrowser -import ClassBrowser from editorpage import EditorPage, classifyws, filename_to_unicode from tabbedpages import get_tabbedpage from configHandler import idleConf @@ -91,11 +89,11 @@ except AttributeError: sys.ps1 = '>>> ' self.menubar = Menu(root) - self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) + self.top = WindowList.ListedToplevel(root, menu=self.menubar) if flist: self.tkinter_vars = flist.vars - #self.top.instance_dict makes flist.inversedict avalable to - #configDialog.py so it can access all EditorWindow instaces + # self.top.instance_dict makes flist.inversedict avalable to + # configDialog.py so it can access all EditorWindow instaces self.top.instance_dict = flist.inversedict else: self.tkinter_vars = {} # keys: Tkinter event names @@ -106,7 +104,7 @@ # create a Notebook where the text pages for this EditorWindow will # reside - self.text_notebook = TabbedPageSet(top) + self.text_notebook = TabbedPageSet(self.top) self.text_notebook.bind('<>', self._update_controls) self.new_tab(filename=filename) @@ -129,13 +127,9 @@ flist.inversedict[self] = key if key: flist.dict[key] = self - text.bind("<>", self._new_callback) - text.bind("<>", self.flist.close_all_callback) - text.bind("<>", self._open_class_browser) - text.bind("<>", self._open_path_browser) self._create_statusbar() - top.after_idle(self.set_line_and_column) + self.top.after_idle(self.set_line_and_column) # usetabs true -> literal tab characters are used by indent and # dedent cmds, possibly mixed with spaces if @@ -259,6 +253,11 @@ return "break" + def new_callback(self, event, page): + dirname, basename = page.io.defaultfilename() + self.flist.new(dirname) + return "break" + def set_line_and_column(self, event=None): # Used by PyShell too line, column = self.current_page.text.index(INSERT).split('.') @@ -696,27 +695,6 @@ command=command, accelerator=accelerator) - def _new_callback(self, event): - dirname, basename = self.current_page.io.defaultfilename() - self.flist.new(dirname) - return "break" - - def _open_class_browser(self, event=None): - filename = self.io.filename - if not filename: - tkMessageBox.showerror( - "No filename", - "This buffer has no associated filename", - master=self.text_notebook) - self.current_page.text.focus_set() - return None - head, tail = os.path.split(filename) - base, ext = os.path.splitext(tail) - ClassBrowser.ClassBrowser(self.flist, base, [head]) - - def _open_path_browser(self, event=None): - PathBrowser.PathBrowser(self.flist) - def __recent_file_callback(self, file_name): def open_recent_file(fn_closure=file_name): self.io.open(editFile=fn_closure) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Sat Aug 2 16:13:41 2008 @@ -6,6 +6,7 @@ import tkSimpleDialog from Tkinter import Text, Menu, TclError +import utils import textView import aboutDialog import configDialog @@ -13,6 +14,8 @@ import PyParse import IOBinding import GrepDialog +import PathBrowser +import ClassBrowser import SearchDialog import ReplaceDialog from configHandler import idleConf @@ -323,31 +326,27 @@ def _setup_bindings(self): text = self.text + def bind_them(to_bind, prefix='_%s'): + for tb in to_bind: + prefix_size = tb.count('<') + method_name = tb[prefix_size:-prefix_size].replace('-', '_') + text.bind(tb, getattr(self, prefix % method_name.lower())) + actions = ('<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>') - - for action in actions: - prefix_size = action.count('<') - method_name = action[prefix_size:-prefix_size].replace('-', '_') - text.bind(action, getattr(self, "_%s" % method_name.lower())) - events = ('<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>') - - for event in events: - prefix_size = event.count('<') - method_name = event[prefix_size:-prefix_size].replace('-', '_') - text.bind(event, getattr(self, "_%s_event" % method_name.lower())) - parent_actions = ('<>', '<>', '<>') + bind_them(actions) + bind_them(events, prefix="_%s_event") for action in parent_actions: prefix_size = action.count('<') method_name = action[prefix_size:-prefix_size].replace('-', '_') @@ -362,6 +361,14 @@ text.event_add("<>", "", "") + if self.editwin.flist: + text.bind("<>", + utils.callback(self.editwin.new_callback, self)) + text.bind("<>", + self.editwin.flist.close_all_callback) + text.bind("<>", self._open_class_browser) + text.bind("<>", self._open_path_browser) + if macosxSupport.runningAsOSXApp(): # Command-W on editorwindows doesn't work without this. text.bind('<>', self.editwin.close_event) @@ -385,6 +392,21 @@ def _about_idle(self, event=None): aboutDialog.AboutDialog(self.text, 'About IDLE') + def _open_class_browser(self, event=None): + filename = self.io.filename + if not filename: + tkMessageBox.showerror("No filename", + "This buffer has no associated filename", + master=self.text) + self.text.focus_set() + return None + head, tail = os.path.split(filename) + base, ext = os.path.splitext(tail) + ClassBrowser.ClassBrowser(self.editwin.flist, base, [head]) + + def _open_path_browser(self, event=None): + PathBrowser.PathBrowser(self.editwin.flist) + def _open_config_dialog(self, event=None): # When changing colors and saving it, it requires the attribute # instance_dict making necessary to pass self.editwin.top as the From buildbot at python.org Sat Aug 2 19:31:48 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 17:31:48 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 3.0 Message-ID: <20080802173148.DDA071E4003@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%203.0/builds/88 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: lars.gustaebel BUILD FAILED: failed svn sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 20:07:39 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 20:07:39 +0200 (CEST) Subject: [Python-checkins] r65413 - sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py Message-ID: <20080802180739.342171E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 20:07:38 2008 New Revision: 65413 Log: name fixes Modified: sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py Sat Aug 2 20:07:38 2008 @@ -94,8 +94,8 @@ "Name Conflict", "You now have multiple edit windows open for %r" % (filename,), master=self.root) - self.dict[newkey] = edit - self.inversedict[edit] = newkey + self.dict[newkey] = editwin + self.inversedict[editwin] = newkey if key: try: del self.dict[key] From python-checkins at python.org Sat Aug 2 20:09:22 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 20:09:22 +0200 (CEST) Subject: [Python-checkins] r65414 - sandbox/trunk/ttk-gsoc/src/idlelib/Debugger.py Message-ID: <20080802180922.D3BE01E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 20:09:22 2008 New Revision: 65414 Log: Loads breakpoints from all tabs now Modified: sandbox/trunk/ttk-gsoc/src/idlelib/Debugger.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/Debugger.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/Debugger.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/Debugger.py Sat Aug 2 20:09:22 2008 @@ -320,12 +320,14 @@ "Load PyShellEditorWindow breakpoints into subprocess debugger" pyshell_edit_windows = self.pyshell.flist.inversedict.keys() for editwin in pyshell_edit_windows: - filename = editwin.io.filename - try: - for lineno in editwin.breakpoints: - self.set_breakpoint_here(filename, lineno) - except AttributeError: - continue + for page in editwin.text_notebook.pages.itervalues(): + editpage = page.editpage + filename = editpage.io.filename + try: + for lineno in editpage.breakpoints: + self.set_breakpoint_here(filename, lineno) + except AttributeError: + continue class StackViewer(ScrolledList): From python-checkins at python.org Sat Aug 2 20:10:05 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 20:10:05 +0200 (CEST) Subject: [Python-checkins] r65415 - sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py Message-ID: <20080802181005.6E7AC1E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 20:10:04 2008 New Revision: 65415 Log: Removed dependence on self.text and self.io from editor window Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py Sat Aug 2 20:10:04 2008 @@ -70,7 +70,7 @@ msgtxt, (lineno, start) = msg self.editwin.gotoline(lineno) self.errorbox("Tabnanny Tokenizing Error", - "Token Error: %s" % msgtxt) + "Token Error: %s" % msgtxt) return False except tabnanny.NannyNag, nag: # The error messages from tabnanny are too confusing... @@ -91,7 +91,7 @@ source = re.sub(r"\r", "\n", source) if source and source[-1] != '\n': source = source + '\n' - text = self.editwin.text + text = self.editwin.current_page.text # XXX check this! text.tag_remove("ERROR", "1.0", "end") try: try: @@ -113,7 +113,7 @@ shell.set_warning_stream(saved_stream) def colorize_syntax_error(self, msg, lineno, offset): - text = self.editwin.text + text = self.editwin.current_page.text # XXX check this! pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1) text.tag_add("ERROR", pos) char = text.get(pos) @@ -175,20 +175,20 @@ If the user has configured IDLE for Autosave, the file will be silently saved if it already exists and is dirty. - """ - filename = self.editwin.io.filename - if not self.editwin.get_saved(): + page = self.editwin.current_page + filename = page.io.filename + if not page.get_saved(): autosave = idleConf.GetOption('main', 'General', 'autosave', type='bool') if autosave and filename: - self.editwin.io.save(None) + page.io.save(None) else: reply = self.ask_save_dialog() - self.editwin.text.focus_set() + page.text.focus_set() if reply == "ok": - self.editwin.io.save(None) - filename = self.editwin.io.filename + page.io.save(None) + filename = page.io.filename else: filename = None return filename @@ -196,14 +196,13 @@ def ask_save_dialog(self): msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?" mb = tkMessageBox.Message(title="Save Before Run or Check", - message=msg, - icon=tkMessageBox.QUESTION, - type=tkMessageBox.OKCANCEL, - default=tkMessageBox.OK, - master=self.editwin.text) + message=msg, icon=tkMessageBox.QUESTION, + type=tkMessageBox.OKCANCEL, default=tkMessageBox.OK, + master=self.editwin.text_notebook) return mb.show() def errorbox(self, title, message): # XXX This should really be a function of EditorWindow... - tkMessageBox.showerror(title, message, master=self.editwin.text) - self.editwin.text.focus_set() + tkMessageBox.showerror(title, message, + master=self.editwin.text_notebook) + self.editwin.current_page.text.focus_set() From python-checkins at python.org Sat Aug 2 20:11:59 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 20:11:59 +0200 (CEST) Subject: [Python-checkins] r65416 - in sandbox/trunk/ttk-gsoc/src/idlelib: EditorWindow.py editorpage.py Message-ID: <20080802181159.9E42A1E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 20:11:59 2008 New Revision: 65416 Log: Moved maybesave from EditorWindow to editorpage; Removed own dependency of self.io in EditorWindow; Updated EditorWindow _close to properly close all the tabs (right now it tries to save even PyShell -- to fix). Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Sat Aug 2 20:11:59 2008 @@ -112,8 +112,6 @@ self.top.focused_widget = self.text self.text_notebook.pack(fill=BOTH, expand=True) - self.io = self.current_page.io - # The following "width" attribute is used by PyShell, so keep it here self.width = idleConf.GetOption('main', 'EditorPage', 'width') @@ -122,7 +120,6 @@ self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<>", self.close_event) - # XXX This need to be done per tab too if flist: flist.inversedict[self] = key if key: @@ -453,7 +450,7 @@ underline=0) def get_saved(self): - return self.current_page.undo.get_saved() # XXX Pretty wrong + return self.current_page.undo.get_saved() # XXX check this! def get_geometry(self): "Return (width, height, x, y)" @@ -465,27 +462,37 @@ def close_event(self, event): self.close() - def maybesave(self): - if self.io: - if not self.get_saved(): - if self.top.state()!='normal': - self.top.deiconify() - self.top.lower() - self.top.lift() - return self.io.maybesave() - def close(self): - reply = self.maybesave() - if str(reply) != "cancel": - self._close() - return reply - - def _close(self): # XXX There should be one like this per EditorPage - if self.io.filename: - self.update_recent_files_list(new_file=self.io.filename) + # XXX need to skip a possible PyShell tab + replies = [] + to_check = self.text_notebook.pages.copy() + while to_check: + curr_tab = self.text_notebook.select() + if TTK: + page_name = self.text_notebook.tab(curr_tab)['text'] + else: + page_name = curr_tab + + page = to_check.pop(page_name) + editpage = page.editpage + reply = str(editpage.maybesave()) + replies.append(reply) + if reply != "cancel": + if editpage.io.filename: + self.update_recent_files_list(new_file=editpage.io.filename) + editpage.close() + self.text_notebook.remove_page(page_name) + + for reply in replies: + if reply == "cancel": + break + else: + self._close() + return replies + + def _close(self): WindowList.unregister_callback(self.postwindowsmenu) - self.unload_extensions() # XXX maybe this should be done per tab - self.text = None + self.unload_extensions() self.tkinter_vars = None for page in self.text_notebook.pages.itervalues(): @@ -627,7 +634,6 @@ def _update_controls(self, event): print self.short_title(), "<<" self.current_page.text.focus_set() - self.io = self.current_page.io self.set_line_and_column() def _create_statusbar(self): @@ -697,7 +703,7 @@ def __recent_file_callback(self, file_name): def open_recent_file(fn_closure=file_name): - self.io.open(editFile=fn_closure) + self.current_page.io.open(editFile=fn_closure) return open_recent_file def __extra_help_callback(self, helpfile): Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Sat Aug 2 20:11:59 2008 @@ -115,6 +115,7 @@ self.per.close() self.per = None + self.text = None # XXX (1) mark where these functions are used def saved_change_hook(self): @@ -185,6 +186,15 @@ def long_title(self): # return unicode string to display non-ASCII chars correctly return filename_to_unicode(self.io.filename or "") + + def maybesave(self): + if self.io: + if not self.get_saved(): + if self.editwin.top.state()!= 'normal': + self.editiwn.top.deiconify() + self.editwin.top.lower() + self.editwin.top.lift() + return self.io.maybesave() # XXX (1) end def center(self, mark="insert"): From python-checkins at python.org Sat Aug 2 20:12:22 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 20:12:22 +0200 (CEST) Subject: [Python-checkins] r65417 - sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Message-ID: <20080802181222.CF6CE1E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 20:12:22 2008 New Revision: 65417 Log: Removed some commented code that was annoying me Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Sat Aug 2 20:12:22 2008 @@ -270,13 +270,6 @@ lineno += 1 return lines -# XXX 13 Dec 2002 KBK Not used currently -# def saved_change_hook(self): -# "Extend base method - clear breaks if module is modified" -# if not self.get_saved(): -# self.clear_file_breaks() -# EditorWindow.saved_change_hook(self) - def _close(self): "Extend base method - clear breaks when module is closed" self.clear_file_breaks() From python-checkins at python.org Sat Aug 2 20:22:10 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 20:22:10 +0200 (CEST) Subject: [Python-checkins] r65418 - sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Message-ID: <20080802182210.F065C1E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 20:22:10 2008 New Revision: 65418 Log: Removed saved_change_hook from post_init so window's title isn't updated till the user moves to that tab Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Sat Aug 2 20:22:10 2008 @@ -100,7 +100,6 @@ self.io.loadfile(filename) else: self.io.set_filename(filename) - self.saved_change_hook() def close(self): """Perform necessary cleanup for this page before closing it.""" From python-checkins at python.org Sat Aug 2 20:23:24 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 20:23:24 +0200 (CEST) Subject: [Python-checkins] r65419 - sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Message-ID: <20080802182324.C65901E4003@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 20:23:24 2008 New Revision: 65419 Log: Update window's title when tab changes Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Sat Aug 2 20:23:24 2008 @@ -108,7 +108,7 @@ self.text_notebook.bind('<>', self._update_controls) self.new_tab(filename=filename) - self.text = text = self.current_page.text # XXX + self.text = self.current_page.text # XXX self.top.focused_widget = self.text self.text_notebook.pack(fill=BOTH, expand=True) @@ -633,7 +633,10 @@ def _update_controls(self, event): print self.short_title(), "<<" - self.current_page.text.focus_set() + curr_page = self.current_page + self.text = curr_page.text + curr_page.saved_change_hook() # update window title + curr_page.text.focus_set() self.set_line_and_column() def _create_statusbar(self): From buildbot at python.org Sat Aug 2 23:17:43 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 21:17:43 +0000 Subject: [Python-checkins] buildbot failure in ia64 Ubuntu 3.0 Message-ID: <20080802211743.A668B1E4003@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%20Ubuntu%203.0/builds/356 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ia64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: antoine.pitrou,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Aborted sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 23:34:41 2008 From: python-checkins at python.org (guilherme.polo) Date: Sat, 2 Aug 2008 23:34:41 +0200 (CEST) Subject: [Python-checkins] r65421 - in sandbox/trunk/ttk-gsoc/src/idlelib: EditorWindow.py PyShell.py Message-ID: <20080802213441.9CC871E4008@bag.python.org> Author: guilherme.polo Date: Sat Aug 2 23:34:40 2008 New Revision: 65421 Log: Moved set_theme to EditorWindow Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Sat Aug 2 23:34:40 2008 @@ -315,6 +315,10 @@ def set_close_hook(self, close_hook): self.close_hook = close_hook + def set_theme(self, ttkstyle): + # called from configDialog.py + ttkstyle.theme_use(idleConf.GetOption('main', 'Theme', 'displaytheme')) + def ResetColorizer(self): "Update the colour theme" # Called from self.filename_change_hook and from configDialog.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Sat Aug 2 23:34:40 2008 @@ -899,10 +899,6 @@ def get_warning_stream(self): return warning_stream - def set_theme(self, ttkstyle): - ttkstyle.theme_use(idleConf.GetOption('main', 'Theme', - 'displaytheme')) - def _toggle_debugger(self, event=None): if self.executing: tkMessageBox.showerror("Don't debug now", From buildbot at python.org Sat Aug 2 23:47:33 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 21:47:33 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian 3.0 Message-ID: <20080802214733.A62511E4003@bag.python.org> The Buildbot has detected a new failure of S-390 Debian 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%203.0/builds/679 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: antoine.pitrou,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "", line 1, in File "/home/pybot/buildarea/3.0.klose-debian-s390/build/Lib/io.py", line 1692, in read decoder.decode(self.buffer.read(), final=True)) File "/home/pybot/buildarea/3.0.klose-debian-s390/build/Lib/io.py", line 923, in read chunk = self.raw.read() IOError: [Errno 9] Bad file descriptor Traceback (most recent call last): File "", line 1, in File "/home/pybot/buildarea/3.0.klose-debian-s390/build/Lib/io.py", line 1692, in read decoder.decode(self.buffer.read(), final=True)) File "/home/pybot/buildarea/3.0.klose-debian-s390/build/Lib/io.py", line 923, in read chunk = self.raw.read() IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Aborted sincerely, -The Buildbot From buildbot at python.org Sat Aug 2 23:53:04 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 21:53:04 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080802215304.E46F81E4003@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/95 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: antoine.pitrou,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "", line 1, in File "C:\buildbot\3.0.heller-windows-amd64\build\lib\multiprocessing\forking.py", line 297, in main self = load(from_parent) File "C:\buildbot\3.0.heller-windows-amd64\build\lib\pickle.py", line 1325, in load return Unpickler(file, encoding=encoding, errors=errors).load() EOFError Traceback (most recent call last): File "../lib/test/regrtest.py", line 1197, in main() File "../lib/test/regrtest.py", line 401, in main sys.stdout.flush() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\io.py", line 1427, in flush self.buffer.flush() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor sincerely, -The Buildbot From python-checkins at python.org Sat Aug 2 23:58:06 2008 From: python-checkins at python.org (antoine.pitrou) Date: Sat, 2 Aug 2008 23:58:06 +0200 (CEST) Subject: [Python-checkins] r65422 - in python/trunk: Lib/test/test_str.py Objects/stringobject.c Message-ID: <20080802215806.45EA31E4003@bag.python.org> Author: antoine.pitrou Date: Sat Aug 2 23:58:05 2008 New Revision: 65422 Log: Preemptively backport the relevant parts of r65420 Modified: python/trunk/Lib/test/test_str.py python/trunk/Objects/stringobject.c Modified: python/trunk/Lib/test/test_str.py ============================================================================== --- python/trunk/Lib/test/test_str.py (original) +++ python/trunk/Lib/test/test_str.py Sat Aug 2 23:58:05 2008 @@ -364,6 +364,9 @@ self.assertRaises(ValueError, format, "", "-") self.assertRaises(ValueError, "{0:=s}".format, '') + def test_buffer_is_readonly(self): + self.assertRaises(TypeError, sys.stdin.readinto, b"") + def test_main(): test_support.run_unittest(StrTest) Modified: python/trunk/Objects/stringobject.c ============================================================================== --- python/trunk/Objects/stringobject.c (original) +++ python/trunk/Objects/stringobject.c Sat Aug 2 23:58:05 2008 @@ -1329,7 +1329,7 @@ string_buffer_getbuffer(PyStringObject *self, Py_buffer *view, int flags) { return PyBuffer_FillInfo(view, (void *)self->ob_sval, Py_SIZE(self), - 0, flags); + 1, flags); } static PySequenceMethods string_as_sequence = { From buildbot at python.org Sun Aug 3 00:04:08 2008 From: buildbot at python.org (buildbot at python.org) Date: Sat, 02 Aug 2008 22:04:08 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080802220408.D56391E4003@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1188 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: antoine.pitrou,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1462, in write self.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Sun Aug 3 02:51:02 2008 From: python-checkins at python.org (brett.cannon) Date: Sun, 3 Aug 2008 02:51:02 +0200 (CEST) Subject: [Python-checkins] r65423 - python/trunk/Lib/urlparse.py Message-ID: <20080803005102.790B51E4003@bag.python.org> Author: brett.cannon Date: Sun Aug 3 02:51:02 2008 New Revision: 65423 Log: Silence some SyntaxWarnings for tuple unpacking in a parameter list for urlparse when run under -3. Modified: python/trunk/Lib/urlparse.py Modified: python/trunk/Lib/urlparse.py ============================================================================== --- python/trunk/Lib/urlparse.py (original) +++ python/trunk/Lib/urlparse.py Sun Aug 3 02:51:02 2008 @@ -173,16 +173,18 @@ _parse_cache[key] = v return v -def urlunparse((scheme, netloc, url, params, query, fragment)): +def urlunparse(data): """Put a parsed URL back together again. This may result in a slightly different, but equivalent URL, if the URL that was parsed originally had redundant delimiters, e.g. a ? with an empty query (the draft states that these are equivalent).""" + scheme, netloc, url, params, query, fragment = data if params: url = "%s;%s" % (url, params) return urlunsplit((scheme, netloc, url, query, fragment)) -def urlunsplit((scheme, netloc, url, query, fragment)): +def urlunsplit(data): + scheme, netloc, url, query, fragment = data if netloc or (scheme and scheme in uses_netloc and url[:2] != '//'): if url and url[:1] != '/': url = '/' + url url = '//' + (netloc or '') + url From python-checkins at python.org Sun Aug 3 02:58:51 2008 From: python-checkins at python.org (brett.cannon) Date: Sun, 3 Aug 2008 02:58:51 +0200 (CEST) Subject: [Python-checkins] r65425 - python/trunk/Lib/pydoc.py Message-ID: <20080803005851.5F6D41E4003@bag.python.org> Author: brett.cannon Date: Sun Aug 3 02:58:51 2008 New Revision: 65425 Log: Silence SyntaxWarning and DeprecationWarning in pydoc triggered by tuple unpacking in parameter lists and using callable(). Found through -3. Modified: python/trunk/Lib/pydoc.py Modified: python/trunk/Lib/pydoc.py ============================================================================== --- python/trunk/Lib/pydoc.py (original) +++ python/trunk/Lib/pydoc.py Sun Aug 3 02:58:51 2008 @@ -173,7 +173,8 @@ def classify_class_attrs(object): """Wrap inspect.classify_class_attrs, with fixup for data descriptors.""" - def fixup((name, kind, cls, value)): + def fixup(data): + name, kind, cls, value = data if inspect.isdatadescriptor(value): kind = 'data descriptor' return name, kind, cls, value @@ -230,7 +231,8 @@ class ErrorDuringImport(Exception): """Errors that occurred while trying to import something to document it.""" - def __init__(self, filename, (exc, value, tb)): + def __init__(self, filename, exc_info): + exc, value, tb = exc_info self.filename = filename self.exc = exc self.value = value @@ -503,8 +505,9 @@ """Make a link for a module.""" return '%s' % (object.__name__, object.__name__) - def modpkglink(self, (name, path, ispackage, shadowed)): + def modpkglink(self, data): """Make a link for a module or package to display in an index.""" + name, path, ispackage, shadowed = data if shadowed: return self.grey(name) if path: @@ -663,12 +666,12 @@ 'Package Contents', '#ffffff', '#aa55cc', contents) elif modules: contents = self.multicolumn( - modules, lambda (key, value), s=self: s.modulelink(value)) + modules, lambda key_value, s=self: s.modulelink(key_value[1])) result = result + self.bigsection( 'Modules', '#ffffff', '#aa55cc', contents) if classes: - classlist = map(lambda (key, value): value, classes) + classlist = map(lambda key_value: key_value[1], classes) contents = [ self.formattree(inspect.getclasstree(classlist, 1), name)] for key, value in classes: @@ -755,7 +758,8 @@ push(msg) for name, kind, homecls, value in ok: base = self.docother(getattr(object, name), name, mod) - if callable(value) or inspect.isdatadescriptor(value): + if (hasattr(value, '__call__') or + inspect.isdatadescriptor(value)): doc = getattr(value, "__doc__", None) else: doc = None @@ -769,7 +773,7 @@ push('\n') return attrs - attrs = filter(lambda (name, kind, cls, value): visiblename(name), + attrs = filter(lambda data: visiblename(data[0]), classify_class_attrs(object)) mdict = {} for key, kind, homecls, value in attrs: @@ -1077,7 +1081,7 @@ 'SUBMODULES', join(submodules, '\n')) if classes: - classlist = map(lambda (key, value): value, classes) + classlist = map(lambda key_value: key_value[1], classes) contents = [self.formattree( inspect.getclasstree(classlist, 1), name)] for key, value in classes: @@ -1173,7 +1177,8 @@ hr.maybe() push(msg) for name, kind, homecls, value in ok: - if callable(value) or inspect.isdatadescriptor(value): + if (hasattr(value, '__call__') or + inspect.isdatadescriptor(value)): doc = getdoc(value) else: doc = None @@ -1181,7 +1186,7 @@ name, mod, maxlen=70, doc=doc) + '\n') return attrs - attrs = filter(lambda (name, kind, cls, value): visiblename(name), + attrs = filter(lambda data: visiblename(data[0]), classify_class_attrs(object)) while attrs: if mro: From buildbot at python.org Sun Aug 3 03:03:40 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 01:03:40 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 trunk Message-ID: <20080803010340.E86431E4003@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%20trunk/builds/92 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Sun Aug 3 03:10:08 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 01:10:08 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP trunk Message-ID: <20080803011008.66C311E4003@bag.python.org> The Buildbot has detected a new failure of amd64 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%20trunk/builds/111 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From buildbot at python.org Sun Aug 3 03:35:00 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 01:35:00 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080803013502.B19071E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/875 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From buildbot at python.org Sun Aug 3 03:58:53 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 01:58:53 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo trunk Message-ID: <20080803015853.5EA0D1E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/1261 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From buildbot at python.org Sun Aug 3 04:03:05 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 02:03:05 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable 3.0 Message-ID: <20080803020306.0BDE21E4003@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%203.0/builds/1297 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_smtplib make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Sun Aug 3 04:21:44 2008 From: python-checkins at python.org (guilherme.polo) Date: Sun, 3 Aug 2008 04:21:44 +0200 (CEST) Subject: [Python-checkins] r65427 - in sandbox/trunk/ttk-gsoc/src/idlelib: ColorDelegator.py configDialog.py configHandler.py Message-ID: <20080803022144.C76C01E4003@bag.python.org> Author: guilherme.polo Date: Sun Aug 3 04:21:44 2008 New Revision: 65427 Log: Added an option to load modules and open files in new tabs instead of creating new windows; Started adding support for bold in theme highlight, but left commented for now. Minor touches around configDialog. Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py sandbox/trunk/ttk-gsoc/src/idlelib/configHandler.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py Sun Aug 3 04:21:44 2008 @@ -182,8 +182,7 @@ ok = False while not ok: mark = next - next = self.index(mark + "+%d lines linestart" % - lines_to_get) + next = self.index(mark + "+%d lines linestart" % lines_to_get) lines_to_get = min(lines_to_get * 2, 100) ok = "SYNC" in self.tag_names(next + "-1c") line = self.get(mark, next) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py Sun Aug 3 04:21:44 2008 @@ -10,10 +10,10 @@ """ from Tkinter import Toplevel, Frame, Button, Scale, Label, LabelFrame, Text, \ Listbox, Scrollbar, Checkbutton, Radiobutton, Entry, \ - StringVar, BooleanVar, IntVar + Checkbutton, StringVar, BooleanVar, IntVar from Tkconstants import LEFT, RIGHT, BOTTOM, TOP, BOTH, GROOVE, SOLID, NONE, \ END, DISABLED, NSEW, Y, X, W, E, HORIZONTAL, NS, EW, \ - ANCHOR, NORMAL + N, ANCHOR, NORMAL import tkMessageBox, tkColorChooser, tkFont from stylist import PoorManStyle @@ -28,7 +28,8 @@ TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') if TTK: from ttk import Frame, Button, Checkbutton, LabelFrame, LabeledScale, \ - Combobox, Entry, Radiobutton, Scrollbar, Label, Style + Combobox, Checkbutton, Entry, Radiobutton, Scrollbar, \ + Label, Style class ConfigDialog(Toplevel): @@ -194,95 +195,107 @@ return frame def CreatePageHighlight(self): - self.builtinTheme=StringVar(self) - self.customTheme=StringVar(self) - self.fgHilite=BooleanVar(self) - self.colour=StringVar(self) - self.fontName=StringVar(self) - self.themeIsBuiltin=BooleanVar(self) - self.highlightTarget=StringVar(self) + self.builtinTheme = StringVar(self) + self.customTheme = StringVar(self) + self.fgHilite = BooleanVar(self) + self.colour = StringVar(self) + self.fontName = StringVar(self) + self.themeIsBuiltin = BooleanVar(self) + self.highlightTarget = StringVar(self) + #self.themeFontBold = BooleanVar(self) ##widget creation #body frame - frame=self.tabPages.pages['Highlighting'].frame + frame = self.tabPages.pages['Highlighting'].frame #body section frames - frameCustom=LabelFrame(frame,borderwidth=2,relief=GROOVE, - text=' Custom Highlighting ') - frameTheme=LabelFrame(frame,borderwidth=2,relief=GROOVE, - text=' Highlighting Theme ') + 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=11, - takefocus=False,highlightthickness=0,wrap=NONE) - text=self.textHighlightSample - text.bind('',lambda e: 'break') - text.bind('',lambda e: 'break') - textAndTags=(('#you can click here','comment'),('\n','normal'), - ('#to choose items','comment'),('\n','normal'),('def','keyword'), - (' ','normal'),('func','definition'),('(param):','normal'), - ('\n ','normal'),('"""string"""','string'),('\n var0 = ','normal'), - ("'string'",'string'),('\n var1 = ','normal'),("'selected'",'hilite'), - ('\n var2 = ','normal'),("'found'",'hit'), - ('\n var3 = ','normal'),('list', 'builtin'), ('(','normal'), - ('None', 'builtin'),(')\n\n','normal'), - (' error ','error'),(' ','normal'),('cursor |','cursor'), - ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'), - (' ','normal'),('stderr','stderr'),('\n','normal')) + self.textHighlightSample = Text(frameCustom, relief=SOLID, + borderwidth=1, font=('courier', 12, ''), cursor='hand2', width=21, + height=11, takefocus=False, highlightthickness=0, wrap=NONE) + text = self.textHighlightSample + text.bind('', lambda e: 'break') + text.bind('', lambda e: 'break') + textAndTags = ( + ('#you can click here','comment'), ('\n','normal'), + ('#to choose items', 'comment'), ('\n', 'normal'), + ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'), + ('(param):', 'normal'), ('\n ', 'normal'), + ('"""string"""', 'string'), + ('\n var0 = ','normal'), ("'string'", 'string'), + ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), + ('\n var2 = ', 'normal'), ("'found'", 'hit'), + ('\n var3 = ', 'normal'), ('list', 'builtin'), ('(', 'normal'), + ('None', 'builtin'), (')\n\n', 'normal'), + (' error ', 'error'), (' ', 'normal'), ('cursor |', 'cursor'), + ('\n ', 'normal'), ('shell', 'console'), (' ', 'normal'), + ('stdout', 'stdout'), (' ', 'normal'), ('stderr', 'stderr'), + ('\n', 'normal') + ) for txTa in textAndTags: - text.insert(END,txTa[0],txTa[1]) + text.insert(END, txTa[0], txTa[1]) for element in self.themeElements.keys(): - text.tag_bind(self.themeElements[element][0],'', - lambda event,elem=element: event.widget.winfo_toplevel() - .highlightTarget.set(elem)) + text.tag_bind(self.themeElements[element][0], '', + lambda event, elem=element: + event.widget.winfo_toplevel().highlightTarget.set(elem)) text.config(state=DISABLED) - self.frameColourSet=Frame(frameCustom, relief=SOLID, borderwidth=1) + self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) self.style(self.frameColourSet, 'Color.TFrame') - frameFgBg=Frame(frameCustom) - buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :', - command=self.GetColour) # XXX - self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet, - self.highlightTarget, None) # XXX - self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite, - value=1,text='Foreground',command=self.SetColourSampleBinding) - self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite, - value=0,text='Background',command=self.SetColourSampleBinding) + frameFgBg = Frame(frameCustom) + buttonSetColour = Button(self.frameColourSet, + text='Choose Colour for :', command=self.GetColour) + self.optMenuHighlightTarget = DynOptionMenu(self.frameColourSet, + self.highlightTarget, None) + #self.optBoldText = Checkbutton(self.frameColourSet, text="Bold", + # variable=self.themeFontBold) + self.radioFg = Radiobutton(frameFgBg, variable=self.fgHilite, + value=1, text='Foreground', command=self.SetColourSampleBinding) + self.radioBg = Radiobutton(frameFgBg, variable=self.fgHilite, + value=0, text='Background', command=self.SetColourSampleBinding) self.fgHilite.set(1) - buttonSaveCustomTheme=Button(frameCustom, - text='Save as New Custom Theme',command=self.SaveAsNewTheme) + buttonSaveCustomTheme = Button(frameCustom, + text='Save as New Custom Theme', command=self.SaveAsNewTheme) #frameTheme - labelTypeTitle=Label(frameTheme,text='Select : ') - self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin, - value=1,command=self.SetThemeType,text='a Built-in Theme') - self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin, - value=0,command=self.SetThemeType,text='a Custom Theme') - self.optMenuThemeBuiltin=DynOptionMenu(frameTheme, - self.builtinTheme,None,command=None) - self.optMenuThemeCustom=DynOptionMenu(frameTheme, - self.customTheme,None,command=None) - self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme', - command=self.DeleteCustomTheme) + labelTypeTitle = Label(frameTheme, text='Select : ') + self.radioThemeBuiltin = Radiobutton(frameTheme, + variable=self.themeIsBuiltin, value=1, + command=self.SetThemeType, text='a Built-in Theme') + self.radioThemeCustom = Radiobutton(frameTheme, + variable=self.themeIsBuiltin, value=0, + command=self.SetThemeType, text='a Custom Theme') + self.optMenuThemeBuiltin = DynOptionMenu(frameTheme, + self.builtinTheme, None, command=None) + self.optMenuThemeCustom = DynOptionMenu(frameTheme, + self.customTheme, None, command=None) + self.buttonDeleteCustomTheme = Button(frameTheme, + text='Delete Custom Theme', command=self.DeleteCustomTheme) ##widget packing #body - frameCustom.pack(side=LEFT,padx=5,pady=5,expand=True,fill=BOTH) - frameTheme.pack(side=LEFT,padx=5,pady=5,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 - 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, + 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, fill=BOTH) - buttonSetColour.pack(side=TOP,expand=True,fill=X,padx=8,pady=4) - self.optMenuHighlightTarget.pack(side=TOP,expand=True,fill=X,padx=8,pady=3) - self.radioFg.pack(side=LEFT,anchor=E) - self.radioBg.pack(side=RIGHT,anchor=W) - buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5) + buttonSetColour.pack(side=TOP, expand=True, fill=X, padx=8, pady=4) + self.optMenuHighlightTarget.pack(side=LEFT, anchor=N, expand=True, + fill=X, padx=8, pady=3) + #self.optBoldText.pack(side=RIGHT, anchor=N, padx=8, pady=3) + self.radioFg.pack(side=LEFT, anchor=E) + self.radioBg.pack(side=RIGHT, anchor=W) + buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) #frameTheme - 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) - self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5) - self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5) - self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,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) + self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) + self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) + self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) return frame def CreatePageKeys(self): @@ -359,15 +372,16 @@ def CreatePageGeneral(self): #tkVars - self.winWidth=StringVar(self) - self.winHeight=StringVar(self) - self.paraWidth=StringVar(self) - self.startupEdit=IntVar(self) - self.autoSave=IntVar(self) - self.encoding=StringVar(self) + self.winWidth = StringVar(self) + self.winHeight = StringVar(self) + self.paraWidth = StringVar(self) + self.startupEdit = IntVar(self) + self.autoSave = IntVar(self) + self.encoding = StringVar(self) self.themename = StringVar(self) - self.userHelpBrowser=BooleanVar(self) - self.helpBrowser=StringVar(self) + self.fileintab = BooleanVar(self) + self.userHelpBrowser = BooleanVar(self) + self.helpBrowser = StringVar(self) #widget creation #body frame=self.tabPages.pages['General'].frame @@ -379,6 +393,7 @@ frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE) frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE) frameEncoding=Frame(frame,borderwidth=2,relief=GROOVE) + frameModTab = Frame(frame, borderwidth=2, relief=GROOVE) if TTK: frameTheme = Frame(frame, borderwidth=2, relief=GROOVE) frameHelp=LabelFrame(frame,borderwidth=2,relief=GROOVE, @@ -417,6 +432,10 @@ value="utf-8",text="UTF-8") radioEncNone=Radiobutton(frameEncoding,variable=self.encoding, value="none",text="None") + # frameModTab + labelMTab = Label(frameModTab, text="Open files and modules in tabs") + checkMTab = Checkbutton(frameModTab, variable=self.fileintab, + text="Yes") #frameTheme if TTK: labelTheme = Label(frameTheme, text="Display Theme") @@ -426,7 +445,7 @@ frameHelpList=Frame(frameHelp) frameHelpListButtons=Frame(frameHelpList) scrollHelpList=Scrollbar(frameHelpList) - self.listHelp=Listbox(frameHelpList,height=5,takefocus=False, + self.listHelp=Listbox(frameHelpList, height=4, takefocus=False, exportselection=False) scrollHelpList.config(command=self.listHelp.yview) self.listHelp.config(yscrollcommand=scrollHelpList.set) @@ -444,6 +463,7 @@ frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X) frameParaSize.pack(side=TOP,padx=5,pady=5,fill=X) frameEncoding.pack(side=TOP,padx=5,pady=5,fill=X) + frameModTab.pack(side=TOP, padx=5, pady=5, fill=X) if TTK: frameTheme.pack(side=TOP, padx=5, pady=5, fill=X) frameHelp.pack(side=TOP,padx=5,pady=5,expand=True,fill=BOTH) @@ -469,7 +489,10 @@ radioEncNone.pack(side=RIGHT,anchor=E,pady=5) radioEncUTF8.pack(side=RIGHT,anchor=E,pady=5) radioEncLocale.pack(side=RIGHT,anchor=E,pady=5) - #franeTheme + #frameModTab + labelMTab.pack(side=LEFT, anchor=W, padx=5, pady=5) + checkMTab.pack(side=RIGHT, anchor=E, padx=5, pady=5) + #frameTheme if TTK: labelTheme.pack(side=LEFT, anchor=W, padx=5, pady=5) comboTheme.pack(side=RIGHT, anchor=E, padx=5, pady=5) @@ -481,6 +504,7 @@ self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5) self.buttonHelpListAdd.pack(side=TOP,anchor=W) self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5) + return frame def AttachVarCallbacks(self): @@ -489,6 +513,7 @@ self.fontBold.trace_variable('w',self.VarChanged_fontBold) self.spaceNum.trace_variable('w',self.VarChanged_spaceNum) self.colour.trace_variable('w',self.VarChanged_colour) + #self.themeFontBold.trace_variable('w', self.VarChanged_themeFontBold) self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme) self.customTheme.trace_variable('w',self.VarChanged_customTheme) self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin) @@ -504,6 +529,7 @@ self.autoSave.trace_variable('w',self.VarChanged_autoSave) self.encoding.trace_variable('w',self.VarChanged_encoding) self.themename.trace_variable('w', self.VarChanged_themename) + self.fileintab.trace_variable('w', self.VarChanged_fileintab) def VarChanged_fontSize(self,*params): value=self.fontSize.get() @@ -524,6 +550,9 @@ def VarChanged_colour(self,*params): self.OnNewColourSet() + #def VarChanged_themeFontBold(self, *params): + # self.OnBoldChanged() + def VarChanged_builtinTheme(self,*params): value=self.builtinTheme.get() self.AddChangedItem('main','Theme','name',value) @@ -605,6 +634,10 @@ value = self.themename.get() self.AddChangedItem('main', 'Theme', 'displaytheme', value) + def VarChanged_fileintab(self, *params): + value = self.fileintab.get() + self.AddChangedItem('main', 'EditorWindow', 'file-in-tab', value) + def ResetChangedItems(self): #When any config item is changed in this dialog, an entry #should be made in the relevant section (config type) of this @@ -614,10 +647,10 @@ self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}} def AddChangedItem(self,type,section,item,value): - value=str(value) #make sure we use a string + value = str(value) #make sure we use a string if not self.changedItems[type].has_key(section): - self.changedItems[type][section]={} - self.changedItems[type][section][item]=value + self.changedItems[type][section] = {} + self.changedItems[type][section][item] = value def GetDefaultItems(self): dItems={'main':{},'highlight':{},'keys':{},'extensions':{}} @@ -756,9 +789,9 @@ def DeleteCustomKeys(self): keySetName=self.customKeys.get() - if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+ - 'to delete the key set %r ?' % (keySetName), - parent=self): + if not tkMessageBox.askyesno("Delete Key Set", + "Are you sure you wish to delete the key set %r ?" % keySetName, + parent=self): return #remove key set from config idleConf.userCfg['keys'].remove_section(keySetName) @@ -767,25 +800,26 @@ #write changes idleConf.userCfg['keys'].Save() #reload user key set list - itemList=idleConf.GetSectionList('user','keys') + itemList = idleConf.GetSectionList('user', 'keys') itemList.sort() if not itemList: self.radioKeysCustom.config(state=DISABLED) - self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -') + self.optMenuKeysCustom.SetMenu(itemList, "- no custom keys -") else: self.optMenuKeysCustom.SetMenu(itemList,itemList[0]) #revert to default key set - self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default')) - self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name')) + self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', + 'default')) + self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) #user can't back out of these changes, they must be applied now self.Apply() self.SetKeysType() def DeleteCustomTheme(self): - themeName=self.customTheme.get() - if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+ - 'to delete the theme %r ?' % (themeName,), - parent=self): + themeName = self.customTheme.get() + if not tkMessageBox.askyesno("Delete Theme", + "Are you sure you wish to delete the theme %r ?" % themeName, + parent=self): return #remove theme from config idleConf.userCfg['highlight'].remove_section(themeName) @@ -794,16 +828,17 @@ #write changes idleConf.userCfg['highlight'].Save() #reload user theme list - itemList=idleConf.GetSectionList('user','highlight') + itemList = idleConf.GetSectionList('user', 'highlight') itemList.sort() if not itemList: self.radioThemeCustom.config(state=DISABLED) - self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -') + self.optMenuThemeCustom.SetMenu(itemList, "- no custom themes -") else: - self.optMenuThemeCustom.SetMenu(itemList,itemList[0]) + self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) #revert to default theme - self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default')) - self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name')) + self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', + 'default')) + self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) #user can't back out of these changes, they must be applied now self.Apply() self.SetThemeType() @@ -833,11 +868,27 @@ if self.fgHilite.get(): plane='foreground' else: plane='background' sampleElement=self.themeElements[self.highlightTarget.get()][0] - self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) + self.textHighlightSample.tag_config(sampleElement, **{plane: newColour}) theme=self.customTheme.get() themeElement=sampleElement+'-'+plane self.AddChangedItem('highlight',theme,themeElement,newColour) + #def OnBoldChanged(self): + # bold = self.themeFontBold.get() and tkFont.BOLD or tkFont.NORMAL + # sampleElement = self.themeElements[self.highlightTarget.get()][0] + + # #fontName = self.fontName.get() + # #self.textHighlightSample.tag_config(sampleElement, + # # font=(fontName, self.fontSize.get(), bold)) + + # self.textHighlightSample.tag_config(sampleElement, + # font=('courier', 12, bold)) + + # theme = self.customTheme.get() + # themeElement = "%s-%s" % (sampleElement, 'bold') + # self.AddChangedItem('highlight', theme, themeElement, + # (bold == tkFont.BOLD) and 1 or 0) + def GetNewThemeName(self,message): # XXX idle bug here usedNames=(idleConf.GetSectionList('user','highlight')+ @@ -898,37 +949,45 @@ self.radioFg.config(state=NORMAL) self.radioBg.config(state=NORMAL) self.fgHilite.set(1) + #tag = self.themeElements[self.highlightTarget.get()][0] + #font = self.textHighlightSample.tag_cget(tag, 'font') + #if font: + # self.themeFontBold.set(font.split()[2] == tkFont.BOLD) + #else: + # self.themeFontBold.set(0) self.SetColourSample() def SetColourSampleBinding(self,*args): self.SetColourSample() def SetColourSample(self): - #set the colour smaple area - tag=self.themeElements[self.highlightTarget.get()][0] - if self.fgHilite.get(): plane='foreground' - else: plane='background' - colour=self.textHighlightSample.tag_cget(tag,plane) + # set the colour sample area + tag = self.themeElements[self.highlightTarget.get()][0] + if self.fgHilite.get(): + plane = 'foreground' + else: + plane = 'background' + colour = self.textHighlightSample.tag_cget(tag, plane) self.ttkstyle.configure('Color.TFrame', background=colour) def PaintThemeSample(self): if self.themeIsBuiltin.get(): #a default theme - theme=self.builtinTheme.get() + theme = self.builtinTheme.get() else: #a user theme - theme=self.customTheme.get() + theme = self.customTheme.get() for elementTitle in self.themeElements.keys(): - element=self.themeElements[elementTitle][0] - colours=idleConf.GetHighlight(theme,element) - if element=='cursor': #cursor sample needs special painting - colours['background']=idleConf.GetHighlight(theme, - 'normal', fgBg='bg') + element = self.themeElements[elementTitle][0] + colours = idleConf.GetHighlight(theme, element) + if element == 'cursor': #cursor sample needs special painting + colours['background'] = idleConf.GetHighlight(theme, + 'normal', fgBg='bg') #handle any unsaved changes to this theme if theme in self.changedItems['highlight'].keys(): - themeDict=self.changedItems['highlight'][theme] - if themeDict.has_key(element+'-foreground'): - colours['foreground']=themeDict[element+'-foreground'] - if themeDict.has_key(element+'-background'): - colours['background']=themeDict[element+'-background'] + themeDict = self.changedItems['highlight'][theme] + if themeDict.has_key(element + '-foreground'): + colours['foreground'] = themeDict[element + '-foreground'] + if themeDict.has_key(element + '-background'): + colours['background'] = themeDict[element + '-background'] self.textHighlightSample.tag_config(element, **colours) self.SetColourSample() @@ -1017,34 +1076,34 @@ def LoadThemeCfg(self): ##current theme type radiobutton - self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default', - type='bool',default=1)) + self.themeIsBuiltin.set(idleConf.GetOption('main', 'Theme', 'default', + type='bool', default=1)) ##currently set theme - currentOption=idleConf.CurrentTheme() + currentOption = idleConf.CurrentTheme() ##load available theme option menus if self.themeIsBuiltin.get(): #default theme selected - itemList=idleConf.GetSectionList('default','highlight') + itemList = idleConf.GetSectionList('default', 'highlight') itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList,currentOption) - itemList=idleConf.GetSectionList('user','highlight') + self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('user', 'highlight') itemList.sort() if not itemList: self.radioThemeCustom.config(state=DISABLED) - self.customTheme.set('- no custom themes -') + self.customTheme.set("- no custom themes -") else: self.optMenuThemeCustom.SetMenu(itemList,itemList[0]) else: #user theme selected - itemList=idleConf.GetSectionList('user','highlight') + itemList = idleConf.GetSectionList('user', 'highlight') itemList.sort() - self.optMenuThemeCustom.SetMenu(itemList,currentOption) - itemList=idleConf.GetSectionList('default','highlight') + self.optMenuThemeCustom.SetMenu(itemList, currentOption) + itemList = idleConf.GetSectionList('default', 'highlight') itemList.sort() - self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0]) + self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) self.SetThemeType() ##load theme element option menu - themeNames=self.themeElements.keys() + themeNames = self.themeElements.keys() themeNames.sort(self.__ThemeNameIndexCompare) - self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0]) + self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) self.PaintThemeSample() self.SetHighlightTarget() @@ -1103,6 +1162,9 @@ # default source encoding self.encoding.set(idleConf.GetOption('main', 'EditorPage', 'encoding', default='none')) + # open files/modules in tabs + self.fileintab.set(idleConf.GetOption('main', 'EditorWindow', + 'file-in-tab', default=1, type='bool')) # additional help sources self.userHelpList = idleConf.GetAllExtraHelpSourcesList() for helpItem in self.userHelpList: Modified: sandbox/trunk/ttk-gsoc/src/idlelib/configHandler.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/configHandler.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/configHandler.py Sun Aug 3 04:21:44 2008 @@ -295,7 +295,10 @@ back=themeDict['normal-background'] else: back=themeDict[element+'-background'] - highlight={"foreground": fore,"background": back} + #bold = 0 + #if element + '-bold' in themeDict: + # bold = themeDict[element + '-bold'] + highlight={"foreground": fore, "background": back}#, "font": bold} if not fgBg: #return dict of both colours return highlight else: #return specified colour only From python-checkins at python.org Sun Aug 3 04:23:39 2008 From: python-checkins at python.org (guilherme.polo) Date: Sun, 3 Aug 2008 04:23:39 +0200 (CEST) Subject: [Python-checkins] r65428 - sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py Message-ID: <20080803022339.4697C1E4003@bag.python.org> Author: guilherme.polo Date: Sun Aug 3 04:23:39 2008 New Revision: 65428 Log: Added a bug note :p Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/ColorDelegator.py Sun Aug 3 04:23:39 2008 @@ -182,6 +182,10 @@ ok = False while not ok: mark = next + # XXX self could be None here in some cases. + # I can reproduce it by clicking "Apply" then "Ok" + # quickly in the config dialog (while editing a not so + # small file). next = self.index(mark + "+%d lines linestart" % lines_to_get) lines_to_get = min(lines_to_get * 2, 100) ok = "SYNC" in self.tag_names(next + "-1c") From python-checkins at python.org Sun Aug 3 04:24:57 2008 From: python-checkins at python.org (guilherme.polo) Date: Sun, 3 Aug 2008 04:24:57 +0200 (CEST) Subject: [Python-checkins] r65429 - in sandbox/trunk/ttk-gsoc/src/idlelib: FileList.py IOBinding.py config-main.def editorpage.py Message-ID: <20080803022457.CF5DE1E4003@bag.python.org> Author: guilherme.polo Date: Sun Aug 3 04:24:57 2008 New Revision: 65429 Log: Adapted some code to work with the new file-in-tab option Modified: sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py sandbox/trunk/ttk-gsoc/src/idlelib/config-main.def sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py Sun Aug 3 04:24:57 2008 @@ -37,9 +37,10 @@ edit = self.dict[key] edit.top.wakeup() return edit + if action: # Don't create window, perform 'action', e.g. open in same window - return action(filename) + return action(filename=filename) else: return self.EditorWindow(self, filename, key) @@ -81,7 +82,7 @@ if not filename: if key: del self.dict[key] - self.inversedict[edit] = None + self.inversedict[editwin] = None return filename = _canonize(filename) newkey = os.path.normcase(filename) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py Sun Aug 3 04:24:57 2008 @@ -206,18 +206,25 @@ filename = editFile if filename: # If the current window has no filename and hasn't been - # modified, we replace its contents (no loss). Otherwise - # we open a new window. But we won't replace the - # shell window (which has an interp(reter) attribute), which - # gets set to "not modified" at every new prompt. + # modified, we replace its contents (no loss). Otherwise + # we open a new window, or maybe open in a tab. + # But we won't replace the shell window (which has an + # interp(reter) attribute), which gets set to "not modified" + # at every new prompt. try: interp = self.editwin.interp except AttributeError: interp = None + if not self.filename and self.get_saved() and not interp: self.editwin.flist.open(filename, self.loadfile) else: - self.editwin.flist.open(filename) + if idleConf.GetOption('main', 'EditorWindow', + 'file-in-tab', default=1, type='bool'): + action = self.editwin.new_tab + else: + action = None + self.editwin.flist.open(filename, action) else: self.text.focus_set() return "break" Modified: sandbox/trunk/ttk-gsoc/src/idlelib/config-main.def ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/config-main.def (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/config-main.def Sun Aug 3 04:24:57 2008 @@ -54,6 +54,7 @@ [EditorWindow] width = 80 height = 40 +file-in-tab = 1 [EditorPage] width= 80 Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Sun Aug 3 04:24:57 2008 @@ -452,9 +452,13 @@ return if f: f.close() - if self.editwin.flist: # XXX - # XXX change this to create a new tab instead - self.editwin.flist.open(file) + if self.editwin.flist: + if idleConf.GetOption('main', 'EditorWindow', 'file-in-tab', + default=1, type='bool'): + action = self.editwin.new_tab + else: + action = None + self.editwin.flist.open(file, action) else: self.io.loadfile(file) From python-checkins at python.org Sun Aug 3 11:21:18 2008 From: python-checkins at python.org (georg.brandl) Date: Sun, 3 Aug 2008 11:21:18 +0200 (CEST) Subject: [Python-checkins] r65430 - python/trunk/Doc/library/site.rst Message-ID: <20080803092118.C7BDD1E4003@bag.python.org> Author: georg.brandl Date: Sun Aug 3 11:21:18 2008 New Revision: 65430 Log: #3495: use current version. Modified: python/trunk/Doc/library/site.rst Modified: python/trunk/Doc/library/site.rst ============================================================================== --- python/trunk/Doc/library/site.rst (original) +++ python/trunk/Doc/library/site.rst Sun Aug 3 11:21:18 2008 @@ -64,8 +64,8 @@ Then the following directories are added to ``sys.path``, in this order:: - /usr/local/lib/python2.3/site-packages/bar - /usr/local/lib/python2.3/site-packages/foo + /usr/local/lib/python2.6/site-packages/bar + /usr/local/lib/python2.6/site-packages/foo Note that :file:`bletch` is omitted because it doesn't exist; the :file:`bar` directory precedes the :file:`foo` directory because :file:`bar.pth` comes From buildbot at python.org Sun Aug 3 12:10:09 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 10:10:09 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080803101009.88FF41E4003@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1191 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Abort trap sincerely, -The Buildbot From buildbot at python.org Sun Aug 3 12:11:53 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 10:11:53 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080803101153.BC3571E4003@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/877 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_smtplib make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Sun Aug 3 18:51:59 2008 From: python-checkins at python.org (guilherme.polo) Date: Sun, 3 Aug 2008 18:51:59 +0200 (CEST) Subject: [Python-checkins] r65434 - in sandbox/trunk/ttk-gsoc/src: 2.x/ttk.py 3.x/ttk.py Message-ID: <20080803165159.5012D1E4012@bag.python.org> Author: guilherme.polo Date: Sun Aug 3 18:51:58 2008 New Revision: 65434 Log: Fixed an old bug that reappeared :/ seems good now Modified: sandbox/trunk/ttk-gsoc/src/2.x/ttk.py sandbox/trunk/ttk-gsoc/src/3.x/ttk.py Modified: sandbox/trunk/ttk-gsoc/src/2.x/ttk.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/2.x/ttk.py (original) +++ sandbox/trunk/ttk-gsoc/src/2.x/ttk.py Sun Aug 3 18:51:58 2008 @@ -12,7 +12,7 @@ of the widgets appearance lies at Themes. """ -__version__ = "0.1.5" +__version__ = "0.1.6" __author__ = "Guilherme Polo " @@ -321,18 +321,12 @@ return _dict_from_tcltuple(res) def _convert_stringval(value): - """Converts a value, that may possibly represents a sequence, - hopefully, to a more appropriate Python object.""" + """Converts a value to, hopefully, a more appropriate Python object.""" + value = unicode(value) try: value = int(value) except (ValueError, TypeError): - if ' ' in value: - value = map(str, value) - try: - value = map(int, value) - except ValueError: - # this value needs no conversion apparently - pass + pass return value @@ -340,19 +334,14 @@ """Returns adict with its values converted from Tcl objects to Python objects.""" for opt, val in adict.iteritems(): - if isinstance(val, basestring): - val = _convert_stringval(val) - - elif val and hasattr(val, '__len__'): + if val and hasattr(val, '__len__') and not isinstance(val, basestring): if getattr(val[0], 'typename', None) == 'StateSpec': val = _list_from_statespec(val) else: - # converts a sequence that possibly has Tcl objects, or not, - # to a better representation - val = map(_convert_stringval, map(str, val)) + val = map(_convert_stringval, val) - elif hasattr(val, 'typename'): # some other Tcl object - val = str(val) + elif hasattr(val, 'typename'): # some other (single) Tcl object + val = _convert_stringval(val) adict[opt] = val Modified: sandbox/trunk/ttk-gsoc/src/3.x/ttk.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/3.x/ttk.py (original) +++ sandbox/trunk/ttk-gsoc/src/3.x/ttk.py Sun Aug 3 18:51:58 2008 @@ -12,7 +12,7 @@ of the widgets appearance lies at Themes. """ -__version__ = "0.1.5" +__version__ = "0.1.6" __author__ = "Guilherme Polo " @@ -321,18 +321,12 @@ return _dict_from_tcltuple(res) def _convert_stringval(value): - """Converts a value, that may possibly represents a sequence, - hopefully, to a more appropriate Python object.""" + """Converts a value to, hopefully, a more appropriate Python object.""" + value = str(value) try: value = int(value) except (ValueError, TypeError): - if ' ' in value: - value = list(map(str, value)) - try: - value = list(map(int, value)) - except ValueError: - # this value needs no conversion apparently - pass + pass return value @@ -340,19 +334,14 @@ """Returns adict with its values converted from Tcl objects to Python objects.""" for opt, val in adict.items(): - if isinstance(val, str): - val = _convert_stringval(val) - - elif val and hasattr(val, '__len__'): + if val and hasattr(val, '__len__') and not isinstance(val, str): if getattr(val[0], 'typename', None) == 'StateSpec': val = _list_from_statespec(val) else: - # converts a sequence that possibly has Tcl objects, or not, - # to a better representation - val = list(map(_convert_stringval, map(str, val))) + val = list(map(_convert_stringval, val)) - elif hasattr(val, 'typename'): # some other Tcl object - val = str(val) + elif hasattr(val, 'typename'): # some other (single) Tcl object + val = _convert_stringval(val) adict[opt] = val From python-checkins at python.org Sun Aug 3 19:51:19 2008 From: python-checkins at python.org (martin.v.loewis) Date: Sun, 3 Aug 2008 19:51:19 +0200 (CEST) Subject: [Python-checkins] r65435 - tracker/roundup-src/roundup/cgi/client.py Message-ID: <20080803175119.06D501E4002@bag.python.org> Author: martin.v.loewis Date: Sun Aug 3 19:51:18 2008 New Revision: 65435 Log: Release DB locks if we don't want to clean. This should prevent the subsequent update of our own session to run into a conflict. Modified: tracker/roundup-src/roundup/cgi/client.py Modified: tracker/roundup-src/roundup/cgi/client.py ============================================================================== --- tracker/roundup-src/roundup/cgi/client.py (original) +++ tracker/roundup-src/roundup/cgi/client.py Sun Aug 3 19:51:18 2008 @@ -410,6 +410,8 @@ hour = 60*60 now = time.time() if now - last_clean < hour: + # Release the database lock obtained when looking at last_clean + self.db.rollback() return # This is a bit ugly, but right now, I'm too lazy to fix a new API From python-checkins at python.org Sun Aug 3 20:48:11 2008 From: python-checkins at python.org (guilherme.polo) Date: Sun, 3 Aug 2008 20:48:11 +0200 (CEST) Subject: [Python-checkins] r65436 - in sandbox/trunk/ttk-gsoc/src/idlelib: EditorWindow.py editorpage.py tabbedpages_new.py tabbedpages_old.py Message-ID: <20080803184811.730151E4002@bag.python.org> Author: guilherme.polo Date: Sun Aug 3 20:48:10 2008 New Revision: 65436 Log: Added support for closing tabs; tab name gets updated now; Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_new.py sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_old.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Sun Aug 3 20:48:10 2008 @@ -110,6 +110,7 @@ self.new_tab(filename=filename) self.text = self.current_page.text # XXX self.top.focused_widget = self.text + self.top.bind('<>', self._post_tab_close) self.text_notebook.pack(fill=BOTH, expand=True) # The following "width" attribute is used by PyShell, so keep it here @@ -196,6 +197,9 @@ def current_page(self): """Return the active EditorPage in EditorWindow.""" curr_tab = self.text_notebook.select() + if not curr_tab: + return None + if TTK: page = self.text_notebook.pages[self.text_notebook.tab( curr_tab)['text']].editpage @@ -227,8 +231,8 @@ page = self.text_notebook.add_page(page_title) vbar = Scrollbar(page.frame, name='vbar') - page.editpage = EditorPage(page.frame, self, name='text', - padx=5, wrap='none') + page.editpage = EditorPage(page.frame, self, title=page_title, + name='text', padx=5, wrap='none') page.editpage.post_init(filename=filename) text = page.editpage.text @@ -257,7 +261,11 @@ def set_line_and_column(self, event=None): # Used by PyShell too - line, column = self.current_page.text.index(INSERT).split('.') + curr_page = self.current_page + if not curr_page: + return + + line, column = curr_page.text.index(INSERT).split('.') self.status_bar.set_label('column', 'Col: %s' % column) self.status_bar.set_label('line', 'Ln: %s' % line) @@ -468,31 +476,19 @@ def close(self): # XXX need to skip a possible PyShell tab - replies = [] to_check = self.text_notebook.pages.copy() + while to_check: curr_tab = self.text_notebook.select() if TTK: page_name = self.text_notebook.tab(curr_tab)['text'] else: page_name = curr_tab - page = to_check.pop(page_name) editpage = page.editpage - reply = str(editpage.maybesave()) - replies.append(reply) - if reply != "cancel": - if editpage.io.filename: - self.update_recent_files_list(new_file=editpage.io.filename) - editpage.close() - self.text_notebook.remove_page(page_name) - - for reply in replies: + reply = editpage.close_tab() if reply == "cancel": break - else: - self._close() - return replies def _close(self): WindowList.unregister_callback(self.postwindowsmenu) @@ -635,11 +631,19 @@ # Private methods + def _post_tab_close(self, event): + if not self.current_page: + # no tabs now, close window + self._close() + return + def _update_controls(self, event): - print self.short_title(), "<<" curr_page = self.current_page + if not curr_page: + return + self.text = curr_page.text - curr_page.saved_change_hook() # update window title + curr_page.saved_change_hook(update_tab_title=False) # update window title curr_page.text.focus_set() self.set_line_and_column() Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Sun Aug 3 20:48:10 2008 @@ -73,8 +73,9 @@ return file, filename, descr class EditorPage(object): - def __init__(self, parent_frame, editwin, **kwargs): + def __init__(self, parent_frame, editwin, title=None, **kwargs): self.editwin = editwin + self.title = title kwargs.setdefault('width', idleConf.GetOption('main', 'EditorPage', 'width')) kwargs.setdefault('height', idleConf.GetOption('main', 'EditorPage', @@ -100,6 +101,19 @@ self.io.loadfile(filename) else: self.io.set_filename(filename) + self.saved_change_hook(False) + + def close_tab(self, event=None): + """Close current tab, if no more tabs present, close the window.""" + reply = str(self.maybesave()) + if reply != "cancel": + if self.io.filename: + self.editwin.update_recent_files_list(new_file=self.io.filename) + self.close() + self.editwin.text_notebook.remove_page(self.title) + self.editwin.top.event_generate('<>') + + return reply def close(self): """Perform necessary cleanup for this page before closing it.""" @@ -117,25 +131,32 @@ self.text = None # XXX (1) mark where these functions are used - def saved_change_hook(self): + def saved_change_hook(self, update_window_title=True, update_tab_title=True): short = self.editwin.short_title() long = self.long_title() if short and long: title = short + " - " + long + tabtitle = os.path.split(long)[-1] elif short: title = short + tabtitle = short elif long: title = long + tabtitle = os.path.split(long)[-1] else: - title = "Untitled" + title = tabtitle = "Untitled" icon = short or long or title if not self.get_saved(): title = "*%s*" % title + tabtitle = "*%s*" % tabtitle icon = "*%s" % icon - self.editwin.top.wm_title(title) - self.editwin.top.wm_iconname(icon) + if update_tab_title: + self.editwin.text_notebook.update_tabtitle(self, tabtitle) + if update_window_title: + self.editwin.top.wm_title(title) + self.editwin.top.wm_iconname(icon) def get_saved(self): return self.undo.get_saved() @@ -144,11 +165,6 @@ self.undo.set_saved(flag) def filename_change_hook(self): - try: - print self, self.editwin, self.editwin.inversedict - except AttributeError: # PyShell - pass - if self.editwin.flist: self.editwin.flist.filename_changed_edit(self, self.editwin) self.saved_change_hook() @@ -341,7 +357,7 @@ method_name = tb[prefix_size:-prefix_size].replace('-', '_') text.bind(tb, getattr(self, prefix % method_name.lower())) - actions = ('<>', '<>', '<>', + actions = ('<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', '<>', @@ -361,6 +377,7 @@ method_name = action[prefix_size:-prefix_size].replace('-', '_') text.bind(action, getattr(self.editwin, method_name)) + text.bind('<>', self.close_tab) text.bind('<>', self.newline_and_indent_event) text.bind("<>", lambda event: "break") text.bind("", self._move_at_edge_if_selection(0)) @@ -382,10 +399,6 @@ # Command-W on editorwindows doesn't work without this. text.bind('<>', self.editwin.close_event) - def _close_tab(self, event): - """Close current tab, if no more tabs present, close the window.""" - print "I do nothing right now" - def _help(self, event=None): fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'help.txt') Modified: sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_new.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_new.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_new.py Sun Aug 3 20:48:10 2008 @@ -36,6 +36,28 @@ for name in page_names: self.add_page(name) + def update_tabtitle(self, tab, newtitle): + """Update tab title to newtitle.""" + currpage = self.pages[tab.title].frame + old = tab.title + + # resolve title duplicate + if newtitle in self.pages and currpage != self.pages[newtitle].frame: + # newtitle is already present, and the current tab is not the + # one who owns it + count = 1 + temptitle = newtitle + while temptitle in self.pages: + if currpage == self.pages[temptitle].frame: + break + temptitle = "%s #%d" % (newtitle, count) + count += 1 + newtitle = temptitle + + tab.title = newtitle + self.pages[newtitle] = self.pages.pop(old) + self.tab(currpage, text=newtitle) + def add_page(self, page_name): """Add a new page with the name given in page_name.""" if not page_name: Modified: sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_old.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_old.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/tabbedpages_old.py Sun Aug 3 20:48:10 2008 @@ -412,6 +412,34 @@ self.change_page(self._default_page) + def update_tabtitle(self, tab, newtitle): + """Update tab title to newtitle.""" + currpage = self.pages[tab.title] + old = tab.title + + # resolve title duplicate + if newtitle in self.pages: + count = 1 + temptitle = newtitle + while temptitle in self.pages: + temptitle = "%s #%d" % (newtitle, count) + count += 1 + newtitle = temptitle + + tab.title = newtitle + # now update 1 million places.. yeh.. + self.pages[newtitle] = self.pages.pop(old) + self._pages_order[self._pages_order.index(old)] = newtitle + self._tab_set._tab_names[self._tab_set._tab_names.index(old)] = newtitle + self._tab_set._tabs[newtitle] = self._tab_set._tabs.pop(old) + self._tab_set._tabs[newtitle].button['text'] = newtitle + if self._tab_set._selected_tab == old: + self._tab_set._selected_tab = newtitle + if self._current_page == old: + self._current_page = newtitle + if self._default_page == old: + self._default_page = newtitle + def add_page(self, page_name): """Add a new page with the name given in page_name.""" if not page_name: @@ -436,7 +464,6 @@ 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: @@ -468,6 +495,7 @@ self.pages[page_name]._show() self._tab_set.set_selected_tab(page_name) + self.event_generate('<>') # conform to ttk.Notebook def last_page(self): return self.pages[self._pages_order[-1]] From python-checkins at python.org Mon Aug 4 00:28:55 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 00:28:55 +0200 (CEST) Subject: [Python-checkins] r65437 - python/trunk/Misc/developers.txt Message-ID: <20080803222855.F3C2A1E4002@bag.python.org> Author: georg.brandl Date: Mon Aug 4 00:28:55 2008 New Revision: 65437 Log: Note the removal of several committers. Modified: python/trunk/Misc/developers.txt Modified: python/trunk/Misc/developers.txt ============================================================================== --- python/trunk/Misc/developers.txt (original) +++ python/trunk/Misc/developers.txt Mon Aug 4 00:28:55 2008 @@ -20,7 +20,7 @@ - Antoine Pitrou was given SVN access on July 16 2008, by recommendation from GvR, for general contributions to Python. -- Jesse Noller was given SVN access on 16 June 2008 by Georg Brandl, +- Jesse Noller was given SVN access on 16 June 2008 by GFB, for work on the multiprocessing module. - Gregor Lingl was given SVN access on 10 June 2008 by MvL, @@ -45,13 +45,13 @@ for work on branches (ast/optimizer related). - Jeroen Ruigrok van der Werven was given SVN access on 12 April 2008 - by Georg Brandl, for documentation work. + by GFB, for documentation work. -- Josiah Carlson was given SVN access on 26 March 2008 by Georg Brandl, +- Josiah Carlson was given SVN access on 26 March 2008 by GFB, for work on asyncore/asynchat. -- Benjamin Peterson was given SVN access on 25 March 2008 by Georg - Brandl, for bug triage work. +- Benjamin Peterson was given SVN access on 25 March 2008 by GFB, + for bug triage work. - Jerry Seutter was given SVN access on 20 March 2008 by BAC, for general contributions to Python. @@ -196,6 +196,12 @@ Permissions Dropped on Request ------------------------------ +- Roy Smith, Matt Fleming and Richard Emslie sent drop requests. + 4 Aug 2008 GFB + +- Per note from Andrew Kuchling, the permissions for Gregory K Johnson + and the Summer Of Code project are no longer needed. 4 Aug 2008 GFB + - Per note from Andrew Kuchling, the permissions for Gregory K Johnson and the Summer Of Code project are no longer needed. AMK will make any future checkins directly. 16 Oct 2005 RDH @@ -235,3 +241,4 @@ TGP: Tim Peters DJG: David Goodger MvL: Martin v. Loewis +GFB: Georg Brandl From python-checkins at python.org Mon Aug 4 00:34:26 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 00:34:26 +0200 (CEST) Subject: [Python-checkins] r65438 - python/trunk/Lib/cookielib.py Message-ID: <20080803223426.23A521E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 00:34:25 2008 New Revision: 65438 Log: Remove a use of list.sort(cmp=) to silence a -3 DeprecationWarning in cookielib. Modified: python/trunk/Lib/cookielib.py Modified: python/trunk/Lib/cookielib.py ============================================================================== --- python/trunk/Lib/cookielib.py (original) +++ python/trunk/Lib/cookielib.py Mon Aug 4 00:34:25 2008 @@ -1258,8 +1258,7 @@ """ # add cookies in order of most specific (ie. longest) path first - def decreasing_size(a, b): return cmp(len(b.path), len(a.path)) - cookies.sort(decreasing_size) + cookies.sort(key=lambda arg: len(arg.path), reverse=True) version_set = False From python-checkins at python.org Mon Aug 4 00:38:20 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 00:38:20 +0200 (CEST) Subject: [Python-checkins] r65440 - python/trunk/Lib/profile.py Message-ID: <20080803223820.12FF81E400B@bag.python.org> Author: brett.cannon Date: Mon Aug 4 00:38:19 2008 New Revision: 65440 Log: Remove a dict.has_key() usage in profile to silence a -3 DeprecationWarning. Modified: python/trunk/Lib/profile.py Modified: python/trunk/Lib/profile.py ============================================================================== --- python/trunk/Lib/profile.py (original) +++ python/trunk/Lib/profile.py Mon Aug 4 00:38:19 2008 @@ -318,7 +318,7 @@ fn = ("", 0, self.c_func_name) self.cur = (t, 0, 0, fn, frame, self.cur) timings = self.timings - if timings.has_key(fn): + if fn in timings: cc, ns, tt, ct, callers = timings[fn] timings[fn] = cc, ns+1, tt, ct, callers else: From python-checkins at python.org Mon Aug 4 00:52:42 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 00:52:42 +0200 (CEST) Subject: [Python-checkins] r65442 - python/trunk/Lib/pstats.py Message-ID: <20080803225242.686221E400C@bag.python.org> Author: brett.cannon Date: Mon Aug 4 00:52:42 2008 New Revision: 65442 Log: Silence -3 warnings in pstats: a dict.has_key() usage and backport solution to move from list.sort(cmp=) to key=. Modified: python/trunk/Lib/pstats.py Modified: python/trunk/Lib/pstats.py ============================================================================== --- python/trunk/Lib/pstats.py (original) +++ python/trunk/Lib/pstats.py Mon Aug 4 00:52:42 2008 @@ -140,7 +140,7 @@ self.total_calls += nc self.prim_calls += cc self.total_tt += tt - if callers.has_key(("jprofile", 0, "profiler")): + if ("jprofile", 0, "profiler") in callers: self.top_level[func] = None if len(func_std_string(func)) > self.max_name_len: self.max_name_len = len(func_std_string(func)) @@ -238,7 +238,7 @@ stats_list.append((cc, nc, tt, ct) + func + (func_std_string(func), func)) - stats_list.sort(TupleComp(sort_tuple).compare) + stats_list.sort(key=CmpToKey(TupleComp(sort_tuple).compare)) self.fcn_list = fcn_list = [] for tuple in stats_list: @@ -471,6 +471,16 @@ return direction return 0 +def CmpToKey(mycmp): + """Convert a cmp= function into a key= function""" + class K(object): + def __init__(self, obj): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) == -1 + return K + + #************************************************************************** # func_name is a triple (file:string, line:int, name:string) From buildbot at python.org Mon Aug 4 00:54:24 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 22:54:24 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian trunk Message-ID: <20080803225424.7AD6C1E4002@bag.python.org> The Buildbot has detected a new failure of S-390 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%20trunk/builds/935 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 00:57:24 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 00:57:24 +0200 (CEST) Subject: [Python-checkins] r65444 - python/trunk/Lib/SimpleXMLRPCServer.py Message-ID: <20080803225724.36A9C1E4010@bag.python.org> Author: brett.cannon Date: Mon Aug 4 00:57:23 2008 New Revision: 65444 Log: Remove a dict.has_key() and callable() usage in SimpleXMLRPCServer as triggered under -3 through test_xmlrpc. Modified: python/trunk/Lib/SimpleXMLRPCServer.py Modified: python/trunk/Lib/SimpleXMLRPCServer.py ============================================================================== --- python/trunk/Lib/SimpleXMLRPCServer.py (original) +++ python/trunk/Lib/SimpleXMLRPCServer.py Mon Aug 4 00:57:23 2008 @@ -141,7 +141,7 @@ return [member for member in dir(obj) if not member.startswith('_') and - callable(getattr(obj, member))] + hasattr(getattr(obj, member), '__call__')] def remove_duplicates(lst): """remove_duplicates([2,2,2,1,3,3]) => [3,1,2] @@ -315,7 +315,7 @@ Returns a string containing documentation for the specified method.""" method = None - if self.funcs.has_key(method_name): + if method_name in self.funcs: method = self.funcs[method_name] elif self.instance is not None: # Instance can implement _methodHelp to return help for a method From python-checkins at python.org Mon Aug 4 00:59:47 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 00:59:47 +0200 (CEST) Subject: [Python-checkins] r65446 - python/trunk/Lib/DocXMLRPCServer.py Message-ID: <20080803225947.3C41D1E4019@bag.python.org> Author: brett.cannon Date: Mon Aug 4 00:59:46 2008 New Revision: 65446 Log: Remove a dict.has_key() use in DocXMLRPCServer that comes up under -3. Modified: python/trunk/Lib/DocXMLRPCServer.py Modified: python/trunk/Lib/DocXMLRPCServer.py ============================================================================== --- python/trunk/Lib/DocXMLRPCServer.py (original) +++ python/trunk/Lib/DocXMLRPCServer.py Mon Aug 4 00:59:46 2008 @@ -175,7 +175,7 @@ methods = {} for method_name in self.system_listMethods(): - if self.funcs.has_key(method_name): + if method_name in self.funcs: method = self.funcs[method_name] elif self.instance is not None: method_info = [None, None] # argspec, documentation From python-checkins at python.org Mon Aug 4 01:15:31 2008 From: python-checkins at python.org (guilherme.polo) Date: Mon, 4 Aug 2008 01:15:31 +0200 (CEST) Subject: [Python-checkins] r65448 - in sandbox/trunk/ttk-gsoc/src/idlelib: AutoComplete.py AutoExpand.py CallTips.py EditorWindow.py FormatParagraph.py HyperParser.py ParenMatch.py ScriptBinding.py ZoomHeight.py Message-ID: <20080803231531.AA8321E400D@bag.python.org> Author: guilherme.polo Date: Mon Aug 4 01:15:31 2008 New Revision: 65448 Log: Extensions work with tabs now Modified: sandbox/trunk/ttk-gsoc/src/idlelib/AutoComplete.py sandbox/trunk/ttk-gsoc/src/idlelib/AutoExpand.py sandbox/trunk/ttk-gsoc/src/idlelib/CallTips.py sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/FormatParagraph.py sandbox/trunk/ttk-gsoc/src/idlelib/HyperParser.py sandbox/trunk/ttk-gsoc/src/idlelib/ParenMatch.py sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py sandbox/trunk/ttk-gsoc/src/idlelib/ZoomHeight.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/AutoComplete.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/AutoComplete.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/AutoComplete.py Mon Aug 4 01:15:31 2008 @@ -38,11 +38,11 @@ popupwait = idleConf.GetOption("extensions", "AutoComplete", "popupwait", type="int", default=0) - def __init__(self, editwin=None): - self.editwin = editwin - if editwin is None: # subprocess and test + def __init__(self, editpage=None): + self.editpage = editpage + if editpage is None: # subprocess and test return - self.text = editwin.text + self.text = editpage.text self.autocompletewindow = None # id of delayed call, and the index of the text insert when the delayed @@ -120,7 +120,7 @@ self.text.after_cancel(self._delayed_completion_id) self._delayed_completion_id = None - hp = HyperParser(self.editwin, "insert") + hp = HyperParser(self.editpage, "insert") curline = self.text.get("insert linestart", "insert") i = j = len(curline) if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): @@ -176,7 +176,7 @@ module may be inoperative if the module was not the last to run. """ try: - rpcclt = self.editwin.flist.pyshell.interp.rpcclt + rpcclt = self.editpage.editwin.flist.pyshell.interp.rpcclt except: rpcclt = None if rpcclt: Modified: sandbox/trunk/ttk-gsoc/src/idlelib/AutoExpand.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/AutoExpand.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/AutoExpand.py Mon Aug 4 01:15:31 2008 @@ -15,8 +15,8 @@ wordchars = string.ascii_letters + string.digits + "_" - def __init__(self, editwin): - self.text = editwin.text + def __init__(self, editpage): + self.text = editpage.text self.state = None def expand_word_event(self, event): Modified: sandbox/trunk/ttk-gsoc/src/idlelib/CallTips.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/CallTips.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/CallTips.py Mon Aug 4 01:15:31 2008 @@ -22,12 +22,12 @@ ]) ] - def __init__(self, editwin=None): - if editwin is None: # subprocess and test - self.editwin = None + def __init__(self, editpage=None): + if editpage is None: # subprocess and test + self.editpage = None return - self.editwin = editwin - self.text = editwin.text + self.editpage = editpage + self.text = editpage.text self.calltip = None self._make_calltip_window = self._make_tk_calltip_window @@ -66,7 +66,7 @@ def open_calltip(self, evalfuncs): self._remove_calltip_window() - hp = HyperParser(self.editwin, "insert") + hp = HyperParser(self.editpage, "insert") sur_paren = hp.get_surrounding_brackets('(') if not sur_paren: return @@ -95,7 +95,7 @@ """ try: - rpcclt = self.editwin.flist.pyshell.interp.rpcclt + rpcclt = self.editpage.editwin.flist.pyshell.interp.rpcclt except: rpcclt = None if rpcclt: Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Mon Aug 4 01:15:31 2008 @@ -105,13 +105,12 @@ # create a Notebook where the text pages for this EditorWindow will # reside self.text_notebook = TabbedPageSet(self.top) - self.text_notebook.bind('<>', - self._update_controls) - self.new_tab(filename=filename) + self.text_notebook.pack(fill=BOTH, expand=True) + self.text_notebook.bind('<>', self._update_controls) + self.new_tab(filename=filename, load_ext=False) self.text = self.current_page.text # XXX self.top.focused_widget = self.text self.top.bind('<>', self._post_tab_close) - self.text_notebook.pack(fill=BOTH, expand=True) # The following "width" attribute is used by PyShell, so keep it here self.width = idleConf.GetOption('main', 'EditorPage', 'width') @@ -175,7 +174,8 @@ self.set_indentation_params(self.ispythonsource(filename)) - self.load_extensions() + self.extensions = {} + self._load_extensions() menu = self.menudict.get('windows') if menu: @@ -225,7 +225,7 @@ return self.text_notebook.select(index - 1) - def new_tab(self, event=None, filename=None): + def new_tab(self, event=None, filename=None, load_ext=True): """Create a new EditorPage and insert it into the notebook.""" page_title = "#%d" % (len(self.text_notebook.pages) + 1) page = self.text_notebook.add_page(page_title) @@ -249,9 +249,10 @@ text.pack(side=TOP, fill=BOTH, expand=1) text.focus_set() - self.apply_bindings() + self.apply_bindings(tab=page) + if load_ext: + self._load_extensions() self.top.event_generate('<>') - return "break" def new_callback(self, event, page): @@ -492,7 +493,7 @@ def _close(self): WindowList.unregister_callback(self.postwindowsmenu) - self.unload_extensions() + self._unload_extensions() self.tkinter_vars = None for page in self.text_notebook.pages.itervalues(): @@ -503,28 +504,8 @@ # unless override: unregister from flist, terminate if last window self.close_hook() - def load_extensions(self): - self.extensions = {} - self.load_standard_extensions() - - def unload_extensions(self): - for ins in self.extensions.values(): - if hasattr(ins, "close"): - ins.close() - self.extensions = {} - - def load_standard_extensions(self): - for name in self.get_standard_extension_names(): - try: - self.load_extension(name) - except: - print "Failed to load extension", repr(name) - traceback.print_exc() - - def get_standard_extension_names(self): - return idleConf.GetExtensions(editor_only=True) - - def load_extension(self, name): + def load_extension(self, name, tab): + #print "load extension", name, tab try: mod = __import__(name, globals(), locals(), []) except ImportError: @@ -534,10 +515,10 @@ keydefs = idleConf.GetExtensionBindings(name) if hasattr(cls, "menudefs"): self._fill_menus(cls.menudefs, keydefs) - ins = cls(self) - self.extensions[name] = ins + ins = cls(tab.editpage) + self.extensions.setdefault(name, []).append(ins) if keydefs: - self.apply_bindings(keydefs) + self.apply_bindings(keydefs, tab) for vevent in keydefs.keys(): methodname = vevent.replace("-", "_") while methodname[:1] == '<': @@ -545,16 +526,19 @@ while methodname[-1:] == '>': methodname = methodname[:-1] methodname = methodname + "_event" - for page in self.text_notebook.pages.itervalues(): - text = page.editpage.text - if hasattr(ins, methodname): - text.bind(vevent, getattr(ins, methodname)) + if hasattr(ins, methodname): + tab.editpage.text.bind(vevent, getattr(ins, methodname)) - def apply_bindings(self, keydefs=None): + def apply_bindings(self, keydefs=None, tab=None): if keydefs is None: keydefs = Bindings.default_keydefs - for page in self.text_notebook.pages.itervalues(): + if tab: + iter_over = [tab] + else: + iter_over = self.text_notebook.pages.itervalues() + + for page in iter_over: text = page.editpage.text text.keydefs = keydefs for event, keylist in keydefs.items(): @@ -631,6 +615,26 @@ # Private methods + def _unload_extensions(self): + for ins in self.extensions.values(): + if hasattr(ins, "close"): + ins.close() + self.extensions = {} + + def _load_extensions(self): + self._load_standard_extensions(self.text_notebook.last_page()) + + def _load_standard_extensions(self, tab): + for name in self._get_standard_extension_names(): + try: + self.load_extension(name, tab) + except: + print "Failed to load extension", repr(name) + traceback.print_exc() + + def _get_standard_extension_names(self): + return idleConf.GetExtensions(editor_only=True) + def _post_tab_close(self, event): if not self.current_page: # no tabs now, close window Modified: sandbox/trunk/ttk-gsoc/src/idlelib/FormatParagraph.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/FormatParagraph.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/FormatParagraph.py Mon Aug 4 01:15:31 2008 @@ -25,28 +25,30 @@ ]) ] - def __init__(self, editwin): - self.editwin = editwin + def __init__(self, editpage): + self.editpage = editpage def close(self): - self.editwin = None + self.editpage = None def format_paragraph_event(self, event): - maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph')) - text = self.editwin.text - first, last = self.editwin.get_selection_indices() + maxformatwidth = int(idleConf.GetOption('main', 'FormatParagraph', + 'paragraph')) + text = self.editpage.text + first, last = self.editpage.get_selection_indices() if first and last: data = text.get(first, last) comment_header = '' else: - first, last, comment_header, data = \ - find_paragraph(text, text.index("insert")) + first, last, comment_header, data = find_paragraph(text, + text.index("insert")) if comment_header: # Reformat the comment lines - convert to text sans header. lines = data.split("\n") lines = map(lambda st, l=len(comment_header): st[l:], lines) data = "\n".join(lines) - # Reformat to maxformatwidth chars or a 20 char width, whichever is greater. + # Reformat to maxformatwidth chars or a 20 char width, whichever is + # greater. format_width = max(maxformatwidth - len(comment_header), 20) newdata = reformat_paragraph(data, format_width) # re-split and re-insert the comment header. Modified: sandbox/trunk/ttk-gsoc/src/idlelib/HyperParser.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/HyperParser.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/HyperParser.py Mon Aug 4 01:15:31 2008 @@ -16,13 +16,13 @@ class HyperParser: - def __init__(self, editwin, index): + def __init__(self, editpage, index): """Initialize the HyperParser to analyze the surroundings of the given index. """ - self.editwin = editwin - self.text = text = editwin.text + self.editwin = editwin = editpage.editwin + self.text = text = editpage.text parser = PyParse.Parser(editwin.indentwidth, editwin.tabwidth) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ParenMatch.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/ParenMatch.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/ParenMatch.py Mon Aug 4 01:15:31 2008 @@ -56,14 +56,13 @@ RESTORE_SEQUENCES = ("", "", "", "") - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text + def __init__(self, editpage): + self.editpage = editpage + self.text = editpage.text # Bind the check-restore event to the function restore_event, # so that we can then use activate_restore (which calls event_add) # and deactivate_restore (which calls event_delete). - editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, - self.restore_event) + editpage.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) self.counter = 0 self.is_restore_active = 0 self.set_style(self.STYLE) @@ -90,7 +89,7 @@ self.set_timeout = self.set_timeout_none def flash_paren_event(self, event): - indices = HyperParser(self.editwin, "insert").get_surrounding_brackets() + indices = HyperParser(self.editpage, "insert").get_surrounding_brackets() if indices is None: self.warn_mismatched() return @@ -103,7 +102,7 @@ closer = self.text.get("insert-1c") if closer not in _openers: return - hp = HyperParser(self.editwin, "insert-1c") + hp = HyperParser(self.editpage, "insert-1c") if not hp.is_in_code(): return indices = hp.get_surrounding_brackets(_openers[closer], True) @@ -159,14 +158,13 @@ if index != self.text.index("insert"): self.handle_restore_timer(c) else: - self.editwin.text_notebook.after(CHECK_DELAY, callme, callme) - self.editwin.text_notebook.after(CHECK_DELAY, callme, callme) + self.text.master.after(CHECK_DELAY, callme, callme) + self.text.master.after(CHECK_DELAY, callme, callme) def set_timeout_last(self): """The last highlight created will be removed after .5 sec""" # associate a counter with an event; only disable the "paren" # tag if the event is for the most recent timer. self.counter += 1 - self.editwin.text_notebook.after(self.FLASH_DELAY, - lambda self=self, c=self.counter: \ - self.handle_restore_timer(c)) + self.text.master.after(self.FLASH_DELAY, + lambda self=self, c=self.counter: self.handle_restore_timer(c)) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/ScriptBinding.py Mon Aug 4 01:15:31 2008 @@ -46,8 +46,9 @@ ('Check Module', '<>'), ('Run Module', '<>'), ]), ] - def __init__(self, editwin): - self.editwin = editwin + def __init__(self, editpage): + self.editpage = editpage + self.editwin = editpage.editwin # Provide instance variables referenced by Debugger # XXX This should be done differently self.flist = self.editwin.flist @@ -91,7 +92,7 @@ source = re.sub(r"\r", "\n", source) if source and source[-1] != '\n': source = source + '\n' - text = self.editwin.current_page.text # XXX check this! + text = self.editpage.text text.tag_remove("ERROR", "1.0", "end") try: try: @@ -113,7 +114,7 @@ shell.set_warning_stream(saved_stream) def colorize_syntax_error(self, msg, lineno, offset): - text = self.editwin.current_page.text # XXX check this! + text = self.editpage.text pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1) text.tag_add("ERROR", pos) char = text.get(pos) @@ -176,7 +177,7 @@ If the user has configured IDLE for Autosave, the file will be silently saved if it already exists and is dirty. """ - page = self.editwin.current_page + page = self.editpage filename = page.io.filename if not page.get_saved(): autosave = idleConf.GetOption('main', 'General', @@ -198,11 +199,10 @@ mb = tkMessageBox.Message(title="Save Before Run or Check", message=msg, icon=tkMessageBox.QUESTION, type=tkMessageBox.OKCANCEL, default=tkMessageBox.OK, - master=self.editwin.text_notebook) + master=self.editpage.text) return mb.show() def errorbox(self, title, message): # XXX This should really be a function of EditorWindow... - tkMessageBox.showerror(title, message, - master=self.editwin.text_notebook) - self.editwin.current_page.text.focus_set() + tkMessageBox.showerror(title, message, master=self.editpage.text) + self.editpage.text.focus_set() Modified: sandbox/trunk/ttk-gsoc/src/idlelib/ZoomHeight.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/ZoomHeight.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/ZoomHeight.py Mon Aug 4 01:15:31 2008 @@ -12,11 +12,11 @@ ]) ] - def __init__(self, editwin): - self.editwin = editwin + def __init__(self, editpage): + self.editpage = editpage def zoom_height_event(self, event): - top = self.editwin.top + top = self.editpage.editwin.top zoom_height(top) def zoom_height(top): From buildbot at python.org Mon Aug 4 01:25:22 2008 From: buildbot at python.org (buildbot at python.org) Date: Sun, 03 Aug 2008 23:25:22 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian 3.0 Message-ID: <20080803232522.EC9BE1E400A@bag.python.org> The Buildbot has detected a new failure of S-390 Debian 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%203.0/builds/683 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 01:27:32 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 01:27:32 +0200 (CEST) Subject: [Python-checkins] r65449 - in python/trunk/Lib/email: base64mime.py charset.py generator.py header.py message.py quoprimime.py Message-ID: <20080803232732.A09801E400F@bag.python.org> Author: brett.cannon Date: Mon Aug 4 01:27:32 2008 New Revision: 65449 Log: Remove Barry's love of deprecated syntax to silence warnings in the email package, when run under -3, about using <>. Modified: python/trunk/Lib/email/base64mime.py python/trunk/Lib/email/charset.py python/trunk/Lib/email/generator.py python/trunk/Lib/email/header.py python/trunk/Lib/email/message.py python/trunk/Lib/email/quoprimime.py Modified: python/trunk/Lib/email/base64mime.py ============================================================================== --- python/trunk/Lib/email/base64mime.py (original) +++ python/trunk/Lib/email/base64mime.py Mon Aug 4 01:27:32 2008 @@ -145,7 +145,7 @@ # BAW: should encode() inherit b2a_base64()'s dubious behavior in # adding a newline to the encoded string? enc = b2a_base64(s[i:i + max_unencoded]) - if enc.endswith(NL) and eol <> NL: + if enc.endswith(NL) and eol != NL: enc = enc[:-1] + eol encvec.append(enc) return EMPTYSTRING.join(encvec) Modified: python/trunk/Lib/email/charset.py ============================================================================== --- python/trunk/Lib/email/charset.py (original) +++ python/trunk/Lib/email/charset.py Mon Aug 4 01:27:32 2008 @@ -253,7 +253,7 @@ Returns "base64" if self.body_encoding is BASE64. Returns "7bit" otherwise. """ - assert self.body_encoding <> SHORTEST + assert self.body_encoding != SHORTEST if self.body_encoding == QP: return 'quoted-printable' elif self.body_encoding == BASE64: @@ -263,7 +263,7 @@ def convert(self, s): """Convert a string from the input_codec to the output_codec.""" - if self.input_codec <> self.output_codec: + if self.input_codec != self.output_codec: return unicode(s, self.input_codec).encode(self.output_codec) else: return s Modified: python/trunk/Lib/email/generator.py ============================================================================== --- python/trunk/Lib/email/generator.py (original) +++ python/trunk/Lib/email/generator.py Mon Aug 4 01:27:32 2008 @@ -211,7 +211,7 @@ # doesn't preserve newlines/continuations in headers. This is no big # deal in practice, but turns out to be inconvenient for the unittest # suite. - if msg.get_boundary() <> boundary: + if msg.get_boundary() != boundary: msg.set_boundary(boundary) # If there's a preamble, write it out, with a trailing CRLF if msg.preamble is not None: Modified: python/trunk/Lib/email/header.py ============================================================================== --- python/trunk/Lib/email/header.py (original) +++ python/trunk/Lib/email/header.py Mon Aug 4 01:27:32 2008 @@ -249,7 +249,7 @@ elif not isinstance(charset, Charset): charset = Charset(charset) # If the charset is our faux 8bit charset, leave the string unchanged - if charset <> '8bit': + if charset != '8bit': # We need to test that the string can be converted to unicode and # back to a byte string, given the input and output codecs of the # charset. @@ -455,7 +455,7 @@ # If this part is longer than maxlen and we aren't already # splitting on whitespace, try to recursively split this line # on whitespace. - if partlen > maxlen and ch <> ' ': + if partlen > maxlen and ch != ' ': subl = _split_ascii(part, maxlen, restlen, continuation_ws, ' ') lines.extend(subl[:-1]) Modified: python/trunk/Lib/email/message.py ============================================================================== --- python/trunk/Lib/email/message.py (original) +++ python/trunk/Lib/email/message.py Mon Aug 4 01:27:32 2008 @@ -252,7 +252,7 @@ charset=charset.get_output_charset()) else: self.set_param('charset', charset.get_output_charset()) - if str(charset) <> charset.get_output_charset(): + if str(charset) != charset.get_output_charset(): self._payload = charset.body_encode(self._payload) if not self.has_key('Content-Transfer-Encoding'): cte = charset.get_body_encoding() @@ -301,7 +301,7 @@ name = name.lower() newheaders = [] for k, v in self._headers: - if k.lower() <> name: + if k.lower() != name: newheaders.append((k, v)) self._headers = newheaders @@ -438,7 +438,7 @@ return self.get_default_type() ctype = paramre.split(value)[0].lower().strip() # RFC 2045, section 5.2 says if its invalid, use text/plain - if ctype.count('/') <> 1: + if ctype.count('/') != 1: return 'text/plain' return ctype @@ -601,7 +601,7 @@ ctype = append_param else: ctype = SEMISPACE.join([ctype, append_param]) - if ctype <> self.get(header): + if ctype != self.get(header): del self[header] self[header] = ctype @@ -617,13 +617,13 @@ return new_ctype = '' for p, v in self.get_params(header=header, unquote=requote): - if p.lower() <> param.lower(): + if p.lower() != param.lower(): if not new_ctype: new_ctype = _formatparam(p, v, requote) else: new_ctype = SEMISPACE.join([new_ctype, _formatparam(p, v, requote)]) - if new_ctype <> self.get(header): + if new_ctype != self.get(header): del self[header] self[header] = new_ctype Modified: python/trunk/Lib/email/quoprimime.py ============================================================================== --- python/trunk/Lib/email/quoprimime.py (original) +++ python/trunk/Lib/email/quoprimime.py Mon Aug 4 01:27:32 2008 @@ -287,7 +287,7 @@ n = len(line) while i < n: c = line[i] - if c <> '=': + if c != '=': decoded += c i += 1 # Otherwise, c == "=". Are we at the end of the line? If so, add From python-checkins at python.org Mon Aug 4 01:40:14 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 01:40:14 +0200 (CEST) Subject: [Python-checkins] r65451 - python/trunk/Lib/email/_parseaddr.py Message-ID: <20080803234014.2F1781E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 01:40:13 2008 New Revision: 65451 Log: Remove a dict.has_key() usage in email._parseaddr found while running -3. Modified: python/trunk/Lib/email/_parseaddr.py Modified: python/trunk/Lib/email/_parseaddr.py ============================================================================== --- python/trunk/Lib/email/_parseaddr.py (original) +++ python/trunk/Lib/email/_parseaddr.py Mon Aug 4 01:40:13 2008 @@ -109,7 +109,7 @@ return None tzoffset = None tz = tz.upper() - if _timezones.has_key(tz): + if tz in _timezones: tzoffset = _timezones[tz] else: try: From python-checkins at python.org Mon Aug 4 01:46:47 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 01:46:47 +0200 (CEST) Subject: [Python-checkins] r65453 - python/trunk/Lib/filecmp.py Message-ID: <20080803234647.62BE91E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 01:46:46 2008 New Revision: 65453 Log: Move filecmp from using dict.has_key() to dict.__contains__() to silence warnings triggered under -3. Modified: python/trunk/Lib/filecmp.py Modified: python/trunk/Lib/filecmp.py ============================================================================== --- python/trunk/Lib/filecmp.py (original) +++ python/trunk/Lib/filecmp.py Mon Aug 4 01:46:46 2008 @@ -132,9 +132,9 @@ def phase1(self): # Compute common names a = dict(izip(imap(os.path.normcase, self.left_list), self.left_list)) b = dict(izip(imap(os.path.normcase, self.right_list), self.right_list)) - self.common = map(a.__getitem__, ifilter(b.has_key, a)) - self.left_only = map(a.__getitem__, ifilterfalse(b.has_key, a)) - self.right_only = map(b.__getitem__, ifilterfalse(a.has_key, b)) + self.common = map(a.__getitem__, ifilter(b.__contains__, a)) + self.left_only = map(a.__getitem__, ifilterfalse(b.__contains__, a)) + self.right_only = map(b.__getitem__, ifilterfalse(a.__contains__, b)) def phase2(self): # Distinguish files, directories, funnies self.common_dirs = [] From python-checkins at python.org Mon Aug 4 01:52:33 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 01:52:33 +0200 (CEST) Subject: [Python-checkins] r65455 - python/trunk/Lib/fileinput.py Message-ID: <20080803235233.407F51E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 01:52:32 2008 New Revision: 65455 Log: Remove a use of callable() in fileinput to silence a -3 warning. Modified: python/trunk/Lib/fileinput.py Modified: python/trunk/Lib/fileinput.py ============================================================================== --- python/trunk/Lib/fileinput.py (original) +++ python/trunk/Lib/fileinput.py Mon Aug 4 01:52:32 2008 @@ -226,7 +226,7 @@ self._mode = mode if inplace and openhook: raise ValueError("FileInput cannot use an opening hook in inplace mode") - elif openhook and not callable(openhook): + elif openhook and not hasattr(openhook, '__call__'): raise ValueError("FileInput openhook must be callable") self._openhook = openhook From buildbot at python.org Mon Aug 4 02:07:43 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 00:07:43 +0000 Subject: [Python-checkins] buildbot failure in ia64 Ubuntu 3.0 Message-ID: <20080804000743.82E221E4002@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%20Ubuntu%203.0/builds/360 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ia64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_tempfile ====================================================================== FAIL: test_fileno (test.test_tempfile.test_SpooledTemporaryFile) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/3.0.klose-debian-ia64/build/Lib/test/test_tempfile.py", line 715, in test_fileno self.failUnless(f.fileno() > 0) AssertionError: None make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 02:09:44 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 02:09:44 +0200 (CEST) Subject: [Python-checkins] r65457 - in python/trunk/Lib/logging: __init__.py config.py Message-ID: <20080804000944.0E3D51E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 02:09:43 2008 New Revision: 65457 Log: Remove dict.has_key() and apply() usage from the logging package to silence warnings when run under -3. Modified: python/trunk/Lib/logging/__init__.py python/trunk/Lib/logging/config.py Modified: python/trunk/Lib/logging/__init__.py ============================================================================== --- python/trunk/Lib/logging/__init__.py (original) +++ python/trunk/Lib/logging/__init__.py Mon Aug 4 02:09:43 2008 @@ -898,7 +898,7 @@ rv = None _acquireLock() try: - if self.loggerDict.has_key(name): + if name in self.loggerDict: rv = self.loggerDict[name] if isinstance(rv, PlaceHolder): ph = rv @@ -926,7 +926,7 @@ rv = None while (i > 0) and not rv: substr = name[:i] - if not self.loggerDict.has_key(substr): + if substr not in self.loggerDict: self.loggerDict[substr] = PlaceHolder(alogger) else: obj = self.loggerDict[substr] @@ -1001,7 +1001,7 @@ logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) """ if self.isEnabledFor(DEBUG): - apply(self._log, (DEBUG, msg, args), kwargs) + self._log(DEBUG, msg, args, **kwargs) def info(self, msg, *args, **kwargs): """ @@ -1013,7 +1013,7 @@ logger.info("Houston, we have a %s", "interesting problem", exc_info=1) """ if self.isEnabledFor(INFO): - apply(self._log, (INFO, msg, args), kwargs) + self._log(INFO, msg, args, **kwargs) def warning(self, msg, *args, **kwargs): """ @@ -1025,7 +1025,7 @@ logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1) """ if self.isEnabledFor(WARNING): - apply(self._log, (WARNING, msg, args), kwargs) + self._log(WARNING, msg, args, **kwargs) warn = warning @@ -1039,13 +1039,13 @@ logger.error("Houston, we have a %s", "major problem", exc_info=1) """ if self.isEnabledFor(ERROR): - apply(self._log, (ERROR, msg, args), kwargs) + self._log(ERROR, msg, args, **kwargs) def exception(self, msg, *args): """ Convenience method for logging an ERROR with exception information. """ - apply(self.error, (msg,) + args, {'exc_info': 1}) + self.error(*((msg,) + args), **{'exc_info': 1}) def critical(self, msg, *args, **kwargs): """ @@ -1057,7 +1057,7 @@ logger.critical("Houston, we have a %s", "major disaster", exc_info=1) """ if self.isEnabledFor(CRITICAL): - apply(self._log, (CRITICAL, msg, args), kwargs) + self._log(CRITICAL, msg, args, **kwargs) fatal = critical @@ -1076,7 +1076,7 @@ else: return if self.isEnabledFor(level): - apply(self._log, (level, msg, args), kwargs) + self._log(level, msg, args, **kwargs) def findCaller(self): """ @@ -1394,7 +1394,7 @@ """ if len(root.handlers) == 0: basicConfig() - apply(root.critical, (msg,)+args, kwargs) + root.critical(*((msg,)+args), **kwargs) fatal = critical @@ -1404,14 +1404,14 @@ """ if len(root.handlers) == 0: basicConfig() - apply(root.error, (msg,)+args, kwargs) + root.error(*((msg,)+args), **kwargs) def exception(msg, *args): """ Log a message with severity 'ERROR' on the root logger, with exception information. """ - apply(error, (msg,)+args, {'exc_info': 1}) + error(*((msg,)+args), **{'exc_info': 1}) def warning(msg, *args, **kwargs): """ @@ -1419,7 +1419,7 @@ """ if len(root.handlers) == 0: basicConfig() - apply(root.warning, (msg,)+args, kwargs) + root.warning(*((msg,)+args), **kwargs) warn = warning @@ -1429,7 +1429,7 @@ """ if len(root.handlers) == 0: basicConfig() - apply(root.info, (msg,)+args, kwargs) + root.info(*((msg,)+args), **kwargs) def debug(msg, *args, **kwargs): """ @@ -1437,7 +1437,7 @@ """ if len(root.handlers) == 0: basicConfig() - apply(root.debug, (msg,)+args, kwargs) + root.debug(*((msg,)+args), **kwargs) def log(level, msg, *args, **kwargs): """ @@ -1445,7 +1445,7 @@ """ if len(root.handlers) == 0: basicConfig() - apply(root.log, (level, msg)+args, kwargs) + root.log(*((level, msg)+args), **kwargs) def disable(level): """ Modified: python/trunk/Lib/logging/config.py ============================================================================== --- python/trunk/Lib/logging/config.py (original) +++ python/trunk/Lib/logging/config.py Mon Aug 4 02:09:43 2008 @@ -152,7 +152,7 @@ klass = _resolve(klass) args = cp.get(sectname, "args") args = eval(args, vars(logging)) - h = apply(klass, args) + h = klass(*args) if "level" in opts: level = cp.get(sectname, "level") h.setLevel(logging._levelNames[level]) From buildbot at python.org Mon Aug 4 02:12:19 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 00:12:19 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable 3.0 Message-ID: <20080804001219.2DFD11E4002@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%203.0/builds/1300 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon,georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_smtplib make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 02:13:29 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 02:13:29 +0200 (CEST) Subject: [Python-checkins] r65459 - in python/trunk: Lib/subprocess.py Misc/NEWS Message-ID: <20080804001329.E943E1E4002@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 02:13:29 2008 New Revision: 65459 Log: - Issue #1857: subprocess.Popen.poll gained an additional _deadstate keyword argument in python 2.5, this broke code that subclassed Popen to include its own poll method. Fixed my moving _deadstate to an _internal_poll method. Modified: python/trunk/Lib/subprocess.py python/trunk/Misc/NEWS Modified: python/trunk/Lib/subprocess.py ============================================================================== --- python/trunk/Lib/subprocess.py (original) +++ python/trunk/Lib/subprocess.py Mon Aug 4 02:13:29 2008 @@ -421,7 +421,7 @@ def _cleanup(): for inst in _active[:]: - if inst.poll(_deadstate=sys.maxint) >= 0: + if inst._internal_poll(_deadstate=sys.maxint) >= 0: try: _active.remove(inst) except ValueError: @@ -635,7 +635,7 @@ # We didn't get to successfully create a child process. return # In case the child hasn't been waited on, check if it's done. - self.poll(_deadstate=sys.maxint) + self._internal_poll(_deadstate=sys.maxint) if self.returncode is None and _active is not None: # Child is still running, keep us alive until we can wait on it. _active.append(self) @@ -671,6 +671,10 @@ return self._communicate(input) + def poll(self): + return self._internal_poll() + + if mswindows: # # Windows methods @@ -842,7 +846,7 @@ errwrite.Close() - def poll(self, _deadstate=None): + def _internal_poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: @@ -1112,7 +1116,7 @@ raise RuntimeError("Unknown child exit status!") - def poll(self, _deadstate=None): + def _internal_poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Mon Aug 4 02:13:29 2008 @@ -71,6 +71,10 @@ file name rather than a ZipInfo instance, so files are extracted with mode 0600 rather than 000 under Unix. +- Issue #1857: subprocess.Popen.poll gained an additional _deadstate keyword + argument in python 2.5, this broke code that subclassed Popen to include its + own poll method. Fixed my moving _deadstate to an _internal_poll method. + Build ----- From python-checkins at python.org Mon Aug 4 02:15:54 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 02:15:54 +0200 (CEST) Subject: [Python-checkins] r65460 - in python/branches/release25-maint: Lib/subprocess.py Misc/NEWS Message-ID: <20080804001554.8DC3C1E4002@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 02:15:54 2008 New Revision: 65460 Log: (backport r65459 from trunk) - Issue #1857: subprocess.Popen.poll gained an additional _deadstate keyword argument in python 2.5, this broke code that subclassed Popen to include its own poll method. Fixed my moving _deadstate to an _internal_poll method. Modified: python/branches/release25-maint/Lib/subprocess.py python/branches/release25-maint/Misc/NEWS Modified: python/branches/release25-maint/Lib/subprocess.py ============================================================================== --- python/branches/release25-maint/Lib/subprocess.py (original) +++ python/branches/release25-maint/Lib/subprocess.py Mon Aug 4 02:15:54 2008 @@ -421,7 +421,7 @@ def _cleanup(): for inst in _active[:]: - if inst.poll(_deadstate=sys.maxint) >= 0: + if inst._internal_poll(_deadstate=sys.maxint) >= 0: try: _active.remove(inst) except ValueError: @@ -634,7 +634,7 @@ # We didn't get to successfully create a child process. return # In case the child hasn't been waited on, check if it's done. - self.poll(_deadstate=sys.maxint) + self._internal_poll(_deadstate=sys.maxint) if self.returncode is None and _active is not None: # Child is still running, keep us alive until we can wait on it. _active.append(self) @@ -670,6 +670,10 @@ return self._communicate(input) + def poll(self): + return self._internal_poll() + + if mswindows: # # Windows methods @@ -843,7 +847,7 @@ errwrite.Close() - def poll(self, _deadstate=None): + def _internal_poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: @@ -1103,7 +1107,7 @@ raise RuntimeError("Unknown child exit status!") - def poll(self, _deadstate=None): + def _internal_poll(self, _deadstate=None): """Check if child process has terminated. Returns returncode attribute.""" if self.returncode is None: Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Mon Aug 4 02:15:54 2008 @@ -126,6 +126,10 @@ in the common case when the data is returned from the underlying socket in increments much smaller than bignumber. +- Issue #1857: subprocess.Popen.poll gained an additional _deadstate keyword + argument in python 2.5, this broke code that subclassed Popen to include its + own poll method. Fixed my moving _deadstate to an _internal_poll method. + Extension Modules ----------------- From python-checkins at python.org Mon Aug 4 02:19:27 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 02:19:27 +0200 (CEST) Subject: [Python-checkins] r65461 - in python/branches/release25-maint: Misc/NEWS PC/_subprocess.c Message-ID: <20080804001927.D7D131E4002@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 02:19:27 2008 New Revision: 65461 Log: (backport trunk r65151) Issue #3120: On 64-bit Windows the subprocess module was truncating handles. Modified: python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/PC/_subprocess.c Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Mon Aug 4 02:19:27 2008 @@ -141,6 +141,8 @@ - issue2858: Fix potential memory corruption when bsddb.db.DBEnv.lock_get and other bsddb.db object constructors raised an exception. +- Issue #3120: On 64-bit Windows the subprocess module was truncating handles. + Tests ----- Modified: python/branches/release25-maint/PC/_subprocess.c ============================================================================== --- python/branches/release25-maint/PC/_subprocess.c (original) +++ python/branches/release25-maint/PC/_subprocess.c Mon Aug 4 02:19:27 2008 @@ -69,6 +69,14 @@ return (PyObject*) self; } +#if defined(MS_WIN32) && !defined(MS_WIN64) +#define HANDLE_TO_PYNUM(handle) PyInt_FromLong((long) handle) +#define PY_HANDLE_PARAM "l" +#else +#define HANDLE_TO_PYNUM(handle) PyLong_FromLongLong((long long) handle) +#define PY_HANDLE_PARAM "L" +#endif + static PyObject* sp_handle_detach(sp_handle_object* self, PyObject* args) { @@ -82,7 +90,7 @@ self->handle = NULL; /* note: return the current handle, as an integer */ - return PyInt_FromLong((long) handle); + return HANDLE_TO_PYNUM(handle); } static PyObject* @@ -122,7 +130,7 @@ static PyObject* sp_handle_as_int(sp_handle_object* self) { - return PyInt_FromLong((long) self->handle); + return HANDLE_TO_PYNUM(self->handle); } static PyNumberMethods sp_handle_as_number; @@ -168,7 +176,7 @@ } /* note: returns integer, not handle object */ - return PyInt_FromLong((long) handle); + return HANDLE_TO_PYNUM(handle); } static PyObject * @@ -186,14 +194,16 @@ HANDLE target_handle; BOOL result; - long source_process_handle; - long source_handle; - long target_process_handle; + HANDLE source_process_handle; + HANDLE source_handle; + HANDLE target_process_handle; int desired_access; int inherit_handle; int options = 0; - if (! PyArg_ParseTuple(args, "lllii|i:DuplicateHandle", + if (! PyArg_ParseTuple(args, + PY_HANDLE_PARAM PY_HANDLE_PARAM PY_HANDLE_PARAM + "ii|i:DuplicateHandle", &source_process_handle, &source_handle, &target_process_handle, @@ -204,9 +214,9 @@ Py_BEGIN_ALLOW_THREADS result = DuplicateHandle( - (HANDLE) source_process_handle, - (HANDLE) source_handle, - (HANDLE) target_process_handle, + source_process_handle, + source_handle, + target_process_handle, &target_handle, desired_access, inherit_handle, @@ -436,13 +446,13 @@ { BOOL result; - long process; + HANDLE process; int exit_code; - if (! PyArg_ParseTuple(args, "li:TerminateProcess", &process, - &exit_code)) + if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:TerminateProcess", + &process, &exit_code)) return NULL; - result = TerminateProcess((HANDLE) process, exit_code); + result = TerminateProcess(process, exit_code); if (! result) return PyErr_SetFromWindowsErr(GetLastError()); @@ -457,11 +467,11 @@ DWORD exit_code; BOOL result; - long process; - if (! PyArg_ParseTuple(args, "l:GetExitCodeProcess", &process)) + HANDLE process; + if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetExitCodeProcess", &process)) return NULL; - result = GetExitCodeProcess((HANDLE) process, &exit_code); + result = GetExitCodeProcess(process, &exit_code); if (! result) return PyErr_SetFromWindowsErr(GetLastError()); @@ -474,15 +484,15 @@ { DWORD result; - long handle; + HANDLE handle; int milliseconds; - if (! PyArg_ParseTuple(args, "li:WaitForSingleObject", + if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM "i:WaitForSingleObject", &handle, &milliseconds)) return NULL; Py_BEGIN_ALLOW_THREADS - result = WaitForSingleObject((HANDLE) handle, (DWORD) milliseconds); + result = WaitForSingleObject(handle, (DWORD) milliseconds); Py_END_ALLOW_THREADS if (result == WAIT_FAILED) @@ -504,13 +514,14 @@ sp_GetModuleFileName(PyObject* self, PyObject* args) { BOOL result; - long module; + HMODULE module; TCHAR filename[MAX_PATH]; - if (! PyArg_ParseTuple(args, "l:GetModuleFileName", &module)) + if (! PyArg_ParseTuple(args, PY_HANDLE_PARAM ":GetModuleFileName", + &module)) return NULL; - result = GetModuleFileName((HMODULE)module, filename, MAX_PATH); + result = GetModuleFileName(module, filename, MAX_PATH); filename[MAX_PATH-1] = '\0'; if (! result) From python-checkins at python.org Mon Aug 4 02:23:59 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 02:23:59 +0200 (CEST) Subject: [Python-checkins] r65462 - python/trunk/Lib/xml/dom/minidom.py Message-ID: <20080804002359.225491E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 02:23:58 2008 New Revision: 65462 Log: Remove dict.has_key() usage in xml.dom.minidom to silence warnings while running under -3. Modified: python/trunk/Lib/xml/dom/minidom.py Modified: python/trunk/Lib/xml/dom/minidom.py ============================================================================== --- python/trunk/Lib/xml/dom/minidom.py (original) +++ python/trunk/Lib/xml/dom/minidom.py Mon Aug 4 02:23:58 2008 @@ -245,7 +245,7 @@ except AttributeError: d = {} self._user_data = d - if d.has_key(key): + if key in d: old = d[key][0] if data is None: # ignore handlers passed for None @@ -562,7 +562,7 @@ _clear_id_cache(self._ownerElement) del self._attrs[n.nodeName] del self._attrsNS[(n.namespaceURI, n.localName)] - if n.__dict__.has_key('ownerElement'): + if 'ownerElement' in n.__dict__: n.__dict__['ownerElement'] = None return n else: @@ -574,7 +574,7 @@ _clear_id_cache(self._ownerElement) del self._attrsNS[(n.namespaceURI, n.localName)] del self._attrs[n.nodeName] - if n.__dict__.has_key('ownerElement'): + if 'ownerElement' in n.__dict__: n.__dict__['ownerElement'] = None return n else: @@ -1664,7 +1664,7 @@ return n def getElementById(self, id): - if self._id_cache.has_key(id): + if id in self._id_cache: return self._id_cache[id] if not (self._elem_info or self._magic_id_count): return None From python-checkins at python.org Mon Aug 4 02:27:29 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 02:27:29 +0200 (CEST) Subject: [Python-checkins] r65464 - python/trunk/Lib/modulefinder.py Message-ID: <20080804002729.5A9641E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 02:27:29 2008 New Revision: 65464 Log: Silence warnings under -3 about using dict.has_key() for modulefinder. Modified: python/trunk/Lib/modulefinder.py Modified: python/trunk/Lib/modulefinder.py ============================================================================== --- python/trunk/Lib/modulefinder.py (original) +++ python/trunk/Lib/modulefinder.py Mon Aug 4 02:27:29 2008 @@ -258,7 +258,7 @@ else: self.msgout(3, "import_module ->", m) return m - if self.badmodules.has_key(fqname): + if fqname in self.badmodules: self.msgout(3, "import_module -> None") return None if parent and parent.__path__ is None: @@ -279,7 +279,8 @@ self.msgout(3, "import_module ->", m) return m - def load_module(self, fqname, fp, pathname, (suffix, mode, type)): + def load_module(self, fqname, fp, pathname, file_info): + suffix, mode, type = file_info self.msgin(2, "load_module", fqname, fp and "fp", pathname) if type == imp.PKG_DIRECTORY: m = self.load_package(fqname, pathname) @@ -460,7 +461,7 @@ return m def add_module(self, fqname): - if self.modules.has_key(fqname): + if fqname in self.modules: return self.modules[fqname] self.modules[fqname] = m = Module(fqname) return m From python-checkins at python.org Mon Aug 4 02:45:34 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 02:45:34 +0200 (CEST) Subject: [Python-checkins] r65466 - in python/branches/release25-maint: Lib/test/test_ioctl.py Misc/NEWS Modules/fcntlmodule.c Message-ID: <20080804004534.849D01E4003@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 02:45:34 2008 New Revision: 65466 Log: (backport of r61652 and r61665 from trunk) Issue #1471: Arguments to fcntl.ioctl are no longer broken on 64-bit OpenBSD and similar platforms due to sign extension. Modified: python/branches/release25-maint/Lib/test/test_ioctl.py python/branches/release25-maint/Misc/NEWS python/branches/release25-maint/Modules/fcntlmodule.c Modified: python/branches/release25-maint/Lib/test/test_ioctl.py ============================================================================== --- python/branches/release25-maint/Lib/test/test_ioctl.py (original) +++ python/branches/release25-maint/Lib/test/test_ioctl.py Mon Aug 4 02:45:34 2008 @@ -14,6 +14,11 @@ except IOError: raise TestSkipped("Unable to open /dev/tty") +try: + import pty +except ImportError: + pty = None + class IoctlTests(unittest.TestCase): def test_ioctl(self): # If this process has been put into the background, TIOCGPGRP returns @@ -34,6 +39,30 @@ self.assertEquals(r, 0) self.assert_(rpgrp in ids, "%s not in %s" % (rpgrp, ids)) + def test_ioctl_signed_unsigned_code_param(self): + if not pty: + raise TestSkipped('pty module required') + mfd, sfd = pty.openpty() + try: + if termios.TIOCSWINSZ < 0: + set_winsz_opcode_maybe_neg = termios.TIOCSWINSZ + set_winsz_opcode_pos = termios.TIOCSWINSZ & 0xffffffffL + else: + set_winsz_opcode_pos = termios.TIOCSWINSZ + set_winsz_opcode_maybe_neg, = struct.unpack("i", + struct.pack("I", termios.TIOCSWINSZ)) + + # We're just testing that these calls do not raise exceptions. + saved_winsz = fcntl.ioctl(mfd, termios.TIOCGWINSZ, "\0"*8) + our_winsz = struct.pack("HHHH",80,25,0,0) + # test both with a positive and potentially negative ioctl code + new_winsz = fcntl.ioctl(mfd, set_winsz_opcode_pos, our_winsz) + new_winsz = fcntl.ioctl(mfd, set_winsz_opcode_maybe_neg, our_winsz) + fcntl.ioctl(mfd, set_winsz_opcode_maybe_neg, saved_winsz) + finally: + os.close(mfd) + os.close(sfd) + def test_main(): run_unittest(IoctlTests) Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Mon Aug 4 02:45:34 2008 @@ -143,6 +143,9 @@ - Issue #3120: On 64-bit Windows the subprocess module was truncating handles. +- Issue #1471: Arguments to fcntl.ioctl are no longer broken on 64-bit OpenBSD + and similar platforms due to sign extension. + Tests ----- Modified: python/branches/release25-maint/Modules/fcntlmodule.c ============================================================================== --- python/branches/release25-maint/Modules/fcntlmodule.c (original) +++ python/branches/release25-maint/Modules/fcntlmodule.c Mon Aug 4 02:45:34 2008 @@ -97,11 +97,20 @@ { #define IOCTL_BUFSZ 1024 int fd; - /* In PyArg_ParseTuple below, use the unsigned int 'I' format for - the signed int 'code' variable, because Python turns 0x8000000 - into a large positive number (PyLong, or PyInt on 64-bit - platforms,) whereas C expects it to be a negative int */ - int code; + /* In PyArg_ParseTuple below, we use the unsigned non-checked 'I' + format for the 'code' parameter because Python turns 0x8000000 + into either a large positive number (PyLong or PyInt on 64-bit + platforms) or a negative number on others (32-bit PyInt) + whereas the system expects it to be a 32bit bit field value + regardless of it being passed as an int or unsigned long on + various platforms. See the termios.TIOCSWINSZ constant across + platforms for an example of thise. + + If any of the 64bit platforms ever decide to use more than 32bits + in their unsigned long ioctl codes this will break and need + special casing based on the platform being built on. + */ + unsigned int code; int arg; int ret; char *str; From python-checkins at python.org Mon Aug 4 02:50:11 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 02:50:11 +0200 (CEST) Subject: [Python-checkins] r65467 - python/trunk/Lib/xmlrpclib.py Message-ID: <20080804005011.A49FE1E4002@bag.python.org> Author: brett.cannon Date: Mon Aug 4 02:50:11 2008 New Revision: 65467 Log: Remove assignment to True/False and use of dict.has_key() to silence warnings while running under -3. Modified: python/trunk/Lib/xmlrpclib.py Modified: python/trunk/Lib/xmlrpclib.py ============================================================================== --- python/trunk/Lib/xmlrpclib.py (original) +++ python/trunk/Lib/xmlrpclib.py Mon Aug 4 02:50:11 2008 @@ -282,10 +282,13 @@ # @param value A boolean value. Any true value is interpreted as True, # all other values are interpreted as False. +from sys import modules +mod_dict = modules[__name__].__dict__ if _bool_is_builtin: boolean = Boolean = bool # to avoid breaking code which references xmlrpclib.{True,False} - True, False = True, False + mod_dict['True'] = True + mod_dict['False'] = False else: class Boolean: """Boolean-value wrapper. @@ -316,7 +319,8 @@ def __nonzero__(self): return self.value - True, False = Boolean(1), Boolean(0) + mod_dict['True'] = Boolean(1) + mod_dict['False'] = Boolean(0) ## # Map true or false value to XML-RPC boolean values. @@ -333,6 +337,8 @@ """Convert any Python value to XML-RPC 'boolean'.""" return _truefalse[operator.truth(value)] +del modules, mod_dict + ## # Wrapper for XML-RPC DateTime values. This converts a time value to # the format used by XML-RPC. @@ -744,7 +750,7 @@ def dump_array(self, value, write): i = id(value) - if self.memo.has_key(i): + if i in self.memo: raise TypeError, "cannot marshal recursive sequences" self.memo[i] = None dump = self.__dump @@ -758,7 +764,7 @@ def dump_struct(self, value, write, escape=escape): i = id(value) - if self.memo.has_key(i): + if i in self.memo: raise TypeError, "cannot marshal recursive dictionaries" self.memo[i] = None dump = self.__dump From buildbot at python.org Mon Aug 4 03:00:19 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 01:00:19 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080804010019.A9CE41E4002@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/880 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 03:03:51 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 03:03:51 +0200 (CEST) Subject: [Python-checkins] r65469 - python/trunk/Doc/library/subprocess.rst Message-ID: <20080804010351.0530E1E4002@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 03:03:50 2008 New Revision: 65469 Log: issue1606: Add warnings to the subprocess documentation about common pitfalls of using pipes that cause deadlocks. Modified: python/trunk/Doc/library/subprocess.rst Modified: python/trunk/Doc/library/subprocess.rst ============================================================================== --- python/trunk/Doc/library/subprocess.rst (original) +++ python/trunk/Doc/library/subprocess.rst Mon Aug 4 03:03:50 2008 @@ -193,6 +193,10 @@ Wait for child process to terminate. Set and return :attr:`returncode` attribute. + warning:: This will deadlock if the child process generates enough output + to a stdout or stderr pipe causing it to block waiting for the OS's pipe + buffer to accept more data. + .. method:: Popen.communicate(input=None) @@ -250,18 +254,30 @@ If the *stdin* argument is ``PIPE``, this attribute is a file object that provides input to the child process. Otherwise, it is ``None``. + warning:: Use :meth:`communicate` rather than .stdin.write() to avoid + deadlocks due to any of the other pipe buffers filling up and blocking the + child process. + .. attribute:: Popen.stdout If the *stdout* argument is ``PIPE``, this attribute is a file object that provides output from the child process. Otherwise, it is ``None``. + warning:: Use :meth:`communicate` rather than .stdout.read() to avoid + deadlocks due to any of the other pipe buffers filling up and blocking the + child process. + .. attribute:: Popen.stderr If the *stderr* argument is ``PIPE``, this attribute is file object that provides error output from the child process. Otherwise, it is ``None``. + warning:: Use :meth:`communicate` rather than .stderr.read() to avoid + deadlocks due to any of the other pipe buffers filling up and blocking the + child process. + .. attribute:: Popen.pid From python-checkins at python.org Mon Aug 4 03:16:25 2008 From: python-checkins at python.org (guilherme.polo) Date: Mon, 4 Aug 2008 03:16:25 +0200 (CEST) Subject: [Python-checkins] r65470 - in sandbox/trunk/ttk-gsoc/src/idlelib: CodeContext.py EditorWindow.py Message-ID: <20080804011625.EDFFD1E4002@bag.python.org> Author: guilherme.polo Date: Mon Aug 4 03:16:25 2008 New Revision: 65470 Log: Fixed CodeContext extension to work with tabs Modified: sandbox/trunk/ttk-gsoc/src/idlelib/CodeContext.py sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/CodeContext.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/CodeContext.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/CodeContext.py Mon Aug 4 03:16:25 2008 @@ -23,7 +23,7 @@ getspacesfirstword =\ lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() -class CodeContext: +class CodeContext(object): menudefs = [('options', [('!Code Conte_xt', '<>')])] context_depth = idleConf.GetOption("extensions", "CodeContext", "numlines", type="int", default=3) @@ -31,10 +31,10 @@ "bgcolor", type="str", default="LightGray") fgcolor = idleConf.GetOption("extensions", "CodeContext", "fgcolor", type="str", default="Black") - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text - self.textfont = self.text["font"] + + def __init__(self, editpage): + self.editpage = editpage + self.editwin = editpage.editwin self.label = None # self.info is a list of (line number, indent level, line text, block # keyword) tuples providing the block structure associated with @@ -43,14 +43,11 @@ # starts the toplevel 'block' of the module. self.info = [(0, -1, "", False)] self.topvisible = 1 - visible = idleConf.GetOption("extensions", "CodeContext", - "visible", type="bool", default=False) + visible = idleConf.GetOption("extensions", "CodeContext", "visible", + type="bool", default=False) if visible: self.toggle_code_context_event() self.editwin.setvar('<>', True) - # Start two update cycles, one for context lines, one for font changes. - self.text.after(UPDATEINTERVAL, self.timer_event) - self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) def toggle_code_context_event(self, event=None): if not self.label: @@ -126,15 +123,12 @@ return lines, lastindent def update_code_context(self): - """Update context information and lines visible in the context pane. - - """ + """Update context information and lines visible in the context pane.""" new_topvisible = int(self.text.index("@0,0").split('.')[0]) if self.topvisible == new_topvisible: # haven't scrolled return if self.topvisible < new_topvisible: # scroll down - lines, lastindent = self.get_context(new_topvisible, - self.topvisible) + lines, lastindent = self.get_context(new_topvisible, self.topvisible) # retain only context info applicable to the region # between topvisible and new_topvisible: while self.info[-1][1] >= lastindent: @@ -147,8 +141,7 @@ stopindent = self.info[-1][1] del self.info[-1] lines, lastindent = self.get_context(new_topvisible, - self.info[-1][0]+1, - stopindent) + self.info[-1][0] + 1, stopindent) self.info.extend(lines) self.topvisible = new_topvisible # empty lines in context pane: @@ -168,3 +161,18 @@ self.textfont = newtextfont self.label["font"] = self.textfont self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) + + # Private methods + + def _get_editpage(self): + return self._editpage + + def _set_editpage(self, page): + self._editpage = page + self.text = page.text + self.textfont = self.text["font"] + # Start two update cycles, one for context lines, one for font changes. + self.text.after(UPDATEINTERVAL, self.timer_event) + self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) + + editpage = property(_get_editpage, _set_editpage) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Mon Aug 4 03:16:25 2008 @@ -505,7 +505,10 @@ self.close_hook() def load_extension(self, name, tab): - #print "load extension", name, tab + load_only_once = ['CodeContext'] + if name in load_only_once and self.extensions.get(name, None): + return + try: mod = __import__(name, globals(), locals(), []) except ImportError: @@ -651,6 +654,11 @@ curr_page.text.focus_set() self.set_line_and_column() + # update references in extensions that are loaded only once + if 'CodeContext' in self.extensions: + ext = self.extensions['CodeContext'][0] + ext.editpage = curr_page + def _create_statusbar(self): self.status_bar = MultiStatusBar(self.top) if macosxSupport.runningAsOSXApp(): From python-checkins at python.org Mon Aug 4 03:29:42 2008 From: python-checkins at python.org (guilherme.polo) Date: Mon, 4 Aug 2008 03:29:42 +0200 (CEST) Subject: [Python-checkins] r65471 - sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Message-ID: <20080804012942.B97671E4002@bag.python.org> Author: guilherme.polo Date: Mon Aug 4 03:29:42 2008 New Revision: 65471 Log: When clicking on menu items, generate events in the active text widget Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Mon Aug 4 03:29:42 2008 @@ -687,7 +687,6 @@ self.reset_help_menu_entries() def _fill_menus(self, menudefs=None, keydefs=None): - # XXX depends on self.text """Add appropriate entries to the menus and submenus Menus that are absent or None in self.menudict are ignored. @@ -697,7 +696,6 @@ if keydefs is None: keydefs = Bindings.default_keydefs menudict = self.menudict - text = self.text for mname, entrylist in menudefs: menu = menudict.get(mname) if not menu: @@ -712,8 +710,8 @@ label = label[1:] underline, label = prepstr(label) accelerator = get_accelerator(keydefs, eventname) - def command(text=text, eventname=eventname): - text.event_generate(eventname) + def command(eventname=eventname): + self.text.event_generate(eventname) if checkbutton: var = self.get_var_obj(eventname, BooleanVar) menu.add_checkbutton(label=label, underline=underline, @@ -721,8 +719,7 @@ variable=var) else: menu.add_command(label=label, underline=underline, - command=command, - accelerator=accelerator) + command=command, accelerator=accelerator) def __recent_file_callback(self, file_name): def open_recent_file(fn_closure=file_name): From python-checkins at python.org Mon Aug 4 03:43:43 2008 From: python-checkins at python.org (andrew.kuchling) Date: Mon, 4 Aug 2008 03:43:43 +0200 (CEST) Subject: [Python-checkins] r65472 - in python/trunk/Lib: mailbox.py test/test_mailbox.py Message-ID: <20080804014343.945F81E4002@bag.python.org> Author: andrew.kuchling Date: Mon Aug 4 03:43:43 2008 New Revision: 65472 Log: Bug 3228: Explicitly supply the file mode to avoid creating executable files, and add corresponding tests. Possible 2.5 backport candidate Modified: python/trunk/Lib/mailbox.py python/trunk/Lib/test/test_mailbox.py Modified: python/trunk/Lib/mailbox.py ============================================================================== --- python/trunk/Lib/mailbox.py (original) +++ python/trunk/Lib/mailbox.py Mon Aug 4 03:43:43 2008 @@ -398,7 +398,8 @@ result = Maildir(path, factory=self._factory) maildirfolder_path = os.path.join(path, 'maildirfolder') if not os.path.exists(maildirfolder_path): - os.close(os.open(maildirfolder_path, os.O_CREAT | os.O_WRONLY)) + os.close(os.open(maildirfolder_path, os.O_CREAT | os.O_WRONLY, + 0666)) return result def remove_folder(self, folder): @@ -1900,7 +1901,7 @@ def _create_carefully(path): """Create a file if it doesn't exist and open for reading and writing.""" - fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR) + fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR, 0666) try: return open(path, 'rb+') finally: Modified: python/trunk/Lib/test/test_mailbox.py ============================================================================== --- python/trunk/Lib/test/test_mailbox.py (original) +++ python/trunk/Lib/test/test_mailbox.py Mon Aug 4 03:43:43 2008 @@ -716,6 +716,16 @@ for msg in self._box: pass + def test_file_perms(self): + # From bug #3228, we want to verify that the file created inside a Maildir + # subfolder isn't marked as executable. + subfolder = self._box.add_folder('subfolder') + path = os.path.join(subfolder._path, 'maildirfolder') + st = os.stat(path) + perms = st.st_mode + self.assertFalse((perms & 0111)) # Execute bits should all be off. + + class _TestMboxMMDF(TestMailbox): def tearDown(self): @@ -805,11 +815,27 @@ self._box.close() - class TestMbox(_TestMboxMMDF): _factory = lambda self, path, factory=None: mailbox.mbox(path, factory) + def test_file_perms(self): + # From bug #3228, we want to verify that the mailbox file isn't executable, + # even if the umask is set to something that would leave executable bits set. + # We only run this test on platforms that support umask. + if hasattr(os, 'umask'): + try: + old_umask = os.umask(0077) + self._box.close() + os.unlink(self._path) + self._box = mailbox.mbox(self._path, create=True) + self._box.add('') + self._box.close() + st = os.stat(self._path) + perms = st.st_mode + self.assertFalse((perms & 0111)) # Execute bits should all be off. + finally: + os.umask(old_umask) class TestMMDF(_TestMboxMMDF): From python-checkins at python.org Mon Aug 4 03:53:15 2008 From: python-checkins at python.org (guilherme.polo) Date: Mon, 4 Aug 2008 03:53:15 +0200 (CEST) Subject: [Python-checkins] r65473 - sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Message-ID: <20080804015315.EA3EA1E4002@bag.python.org> Author: guilherme.polo Date: Mon Aug 4 03:53:15 2008 New Revision: 65473 Log: Fixed an annoying bug that was causing FileList to print "Don't know this EditorWindow ..." Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Mon Aug 4 03:53:15 2008 @@ -102,6 +102,13 @@ self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), 'recent-files.lst') + if flist: + flist.inversedict[self] = key + if key: + flist.dict[key] = self + + self.menudict = None + # create a Notebook where the text pages for this EditorWindow will # reside self.text_notebook = TabbedPageSet(self.top) @@ -115,16 +122,9 @@ # The following "width" attribute is used by PyShell, so keep it here self.width = idleConf.GetOption('main', 'EditorPage', 'width') - self._createmenubar() - self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.bind("<>", self.close_event) - if flist: - flist.inversedict[self] = key - if key: - flist.dict[key] = self - self._create_statusbar() self.top.after_idle(self.set_line_and_column) @@ -165,13 +165,6 @@ # Making the initial values larger slows things down more often. self.num_context_lines = 50, 500, 5000000 - # Create the recent files submenu - self.recent_files_menu = Menu(self.menubar) - self.menudict['file'].insert_cascade(3, label='Recent Files', - underline=0, - menu=self.recent_files_menu) - self.update_recent_files_list() - self.set_indentation_params(self.ispythonsource(filename)) self.extensions = {} @@ -233,10 +226,21 @@ vbar = Scrollbar(page.frame, name='vbar') page.editpage = EditorPage(page.frame, self, title=page_title, name='text', padx=5, wrap='none') + + if self.menudict is None: + # This EditorWindow is being created now, perform the following + # tasks before. + self.menudict = {} + self._createmenubar(page.editpage.text) + # Create the recent files submenu + self.recent_files_menu = Menu(self.menubar) + self.menudict['file'].insert_cascade(3, label='Recent Files', + underline=0, menu=self.recent_files_menu) + self.update_recent_files_list() + page.editpage.post_init(filename=filename) text = page.editpage.text - vbar['command'] = text.yview vbar.pack(side=RIGHT, fill=Y) text['yscrollcommand'] = vbar.set @@ -563,11 +567,11 @@ else: raise NameError, name - def get_var_obj(self, name, vartype=None): # XXX depends on self.text + def get_var_obj(self, name, vartype=None, text=None): var = self.tkinter_vars.get(name) if not var and vartype: # create a Tkinter variable object with self.text as master: - self.tkinter_vars[name] = var = vartype(self.text) + self.tkinter_vars[name] = var = vartype(text or self.text) return var # Tk implementations of "virtual text methods" -- each platform @@ -669,9 +673,9 @@ self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) self.status_bar.pack(side=BOTTOM, fill=X) - def _createmenubar(self): + def _createmenubar(self, text): mbar = self.menubar - self.menudict = menudict = {} + menudict = self.menudict for name, label in self.menu_specs: underline, label = prepstr(label) menudict[name] = menu = Menu(mbar, name=name, tearoff=0) @@ -682,11 +686,11 @@ menudict['application'] = menu = Menu(mbar, name='apple') mbar.add_cascade(label='IDLE', menu=menu) - self._fill_menus() + self._fill_menus(text=text) self.base_helpmenu_length = self.menudict['help'].index(END) self.reset_help_menu_entries() - def _fill_menus(self, menudefs=None, keydefs=None): + def _fill_menus(self, menudefs=None, keydefs=None, text=None): """Add appropriate entries to the menus and submenus Menus that are absent or None in self.menudict are ignored. @@ -713,7 +717,7 @@ def command(eventname=eventname): self.text.event_generate(eventname) if checkbutton: - var = self.get_var_obj(eventname, BooleanVar) + var = self.get_var_obj(eventname, BooleanVar, text) menu.add_checkbutton(label=label, underline=underline, command=command, accelerator=accelerator, variable=var) From python-checkins at python.org Mon Aug 4 03:54:17 2008 From: python-checkins at python.org (guilherme.polo) Date: Mon, 4 Aug 2008 03:54:17 +0200 (CEST) Subject: [Python-checkins] r65474 - sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Message-ID: <20080804015417.A5D481E4003@bag.python.org> Author: guilherme.polo Date: Mon Aug 4 03:54:17 2008 New Revision: 65474 Log: Check if current_page is still valid before using it. One situation it may be invalid is when requesting to close IDLE while Debugger window is open. Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Mon Aug 4 03:54:17 2008 @@ -1214,6 +1214,9 @@ self.console.write(s) curr_page = self.current_page + if not curr_page: + return + curr_page.text.mark_set("insert", "end-1c") self.set_line_and_column() curr_page.io.reset_undo() From buildbot at python.org Mon Aug 4 03:57:18 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 01:57:18 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 trunk Message-ID: <20080804015718.88BEF1E4002@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%20trunk/builds/97 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 04:28:20 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 02:28:20 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 3.0 Message-ID: <20080804022820.DBDD61E4003@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%203.0/builds/98 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed svn sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 04:40:50 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 02:40:50 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable 2.5 Message-ID: <20080804024050.4F8431E4003@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%202.5/builds/260 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: gregory.p.smith BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_socketserver make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 05:00:54 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 03:00:54 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080804030055.3C7761E4003@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/101 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 05:17:59 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 03:17:59 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 2.5 Message-ID: <20080804031759.621CC1E4003@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%202.5/builds/6 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: gregory.p.smith BUILD FAILED: failed svn sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 05:23:26 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 05:23:26 +0200 (CEST) Subject: [Python-checkins] r65475 - in python/branches/release25-maint: Lib/subprocess.py Misc/NEWS Message-ID: <20080804032326.5DD751E4003@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 05:23:25 2008 New Revision: 65475 Log: (backport from trunk r64756) Issue #2113: Fix error in subprocess.Popen if the select system call is interrupted by a signal. Modified: python/branches/release25-maint/Lib/subprocess.py python/branches/release25-maint/Misc/NEWS Modified: python/branches/release25-maint/Lib/subprocess.py ============================================================================== --- python/branches/release25-maint/Lib/subprocess.py (original) +++ python/branches/release25-maint/Lib/subprocess.py Mon Aug 4 05:23:25 2008 @@ -1153,7 +1153,12 @@ input_offset = 0 while read_set or write_set: - rlist, wlist, xlist = select.select(read_set, write_set, []) + try: + rlist, wlist, xlist = select.select(read_set, write_set, []) + except select.error, e: + if e.args[0] == errno.EINTR: + continue + raise if self.stdin in wlist: # When select has indicated that the file is writable, Modified: python/branches/release25-maint/Misc/NEWS ============================================================================== --- python/branches/release25-maint/Misc/NEWS (original) +++ python/branches/release25-maint/Misc/NEWS Mon Aug 4 05:23:25 2008 @@ -130,6 +130,10 @@ argument in python 2.5, this broke code that subclassed Popen to include its own poll method. Fixed my moving _deadstate to an _internal_poll method. +- Issue #2113: Fix error in subprocess.Popen if the select system call is + interrupted by a signal. + + Extension Modules ----------------- From buildbot at python.org Mon Aug 4 05:39:40 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 03:39:40 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable 3.0 Message-ID: <20080804033941.0EC851E4003@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%203.0/builds/1302 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Aborted sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 06:01:57 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 04:01:57 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP trunk Message-ID: <20080804040158.171DD1E4003@bag.python.org> The Buildbot has detected a new failure of amd64 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%20trunk/builds/115 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling,brett.cannon,gregory.p.smith BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 06:05:48 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 04:05:48 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 2.5 Message-ID: <20080804040548.951561E4003@bag.python.org> The Buildbot has detected a new failure of amd64 XP 2.5. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%202.5/builds/6 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/release25-maint] HEAD Blamelist: gregory.p.smith BUILD FAILED: failed compile sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 08:29:36 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 08:29:36 +0200 (CEST) Subject: [Python-checkins] r65476 - python/trunk/Doc/library/subprocess.rst Message-ID: <20080804062936.C67751E4003@bag.python.org> Author: georg.brandl Date: Mon Aug 4 08:29:36 2008 New Revision: 65476 Log: Fix markup. Modified: python/trunk/Doc/library/subprocess.rst Modified: python/trunk/Doc/library/subprocess.rst ============================================================================== --- python/trunk/Doc/library/subprocess.rst (original) +++ python/trunk/Doc/library/subprocess.rst Mon Aug 4 08:29:36 2008 @@ -193,9 +193,11 @@ Wait for child process to terminate. Set and return :attr:`returncode` attribute. - warning:: This will deadlock if the child process generates enough output - to a stdout or stderr pipe causing it to block waiting for the OS's pipe - buffer to accept more data. + .. warning:: + + This will deadlock if the child process generates enough output to a + stdout or stderr pipe causing it to block waiting for the OS's pipe buffer + to accept more data. .. method:: Popen.communicate(input=None) @@ -249,35 +251,30 @@ The following attributes are also available: +.. warning:: + + Use :meth:`communicate` rather than ``.stdin.write()``, ``.stdout.read()`` or + ``.stderr.read`` to avoid deadlocks due to any of the other pipe buffers + filling up and blocking the child process. + + .. attribute:: Popen.stdin If the *stdin* argument is ``PIPE``, this attribute is a file object that provides input to the child process. Otherwise, it is ``None``. - warning:: Use :meth:`communicate` rather than .stdin.write() to avoid - deadlocks due to any of the other pipe buffers filling up and blocking the - child process. - .. attribute:: Popen.stdout If the *stdout* argument is ``PIPE``, this attribute is a file object that provides output from the child process. Otherwise, it is ``None``. - warning:: Use :meth:`communicate` rather than .stdout.read() to avoid - deadlocks due to any of the other pipe buffers filling up and blocking the - child process. - .. attribute:: Popen.stderr If the *stderr* argument is ``PIPE``, this attribute is file object that provides error output from the child process. Otherwise, it is ``None``. - warning:: Use :meth:`communicate` rather than .stderr.read() to avoid - deadlocks due to any of the other pipe buffers filling up and blocking the - child process. - .. attribute:: Popen.pid From python-checkins at python.org Mon Aug 4 09:23:29 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 09:23:29 +0200 (CEST) Subject: [Python-checkins] r65477 - python/trunk/Doc/library/tempfile.rst Message-ID: <20080804072329.E93531E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 09:23:29 2008 New Revision: 65477 Log: Template is always "tmp". Modified: python/trunk/Doc/library/tempfile.rst Modified: python/trunk/Doc/library/tempfile.rst ============================================================================== --- python/trunk/Doc/library/tempfile.rst (original) +++ python/trunk/Doc/library/tempfile.rst Mon Aug 4 09:23:29 2008 @@ -233,8 +233,7 @@ When set to a value other than ``None``, this variable defines the prefix of the final component of the filenames returned by :func:`mktemp`. A string of six random letters and digits is appended to the prefix to make the filename unique. - On Windows, the default prefix is :file:`~T`; on all other systems it is - :file:`tmp`. + The default prefix is :file:`tmp`. Older versions of this module used to require that ``template`` be set to ``None`` after a call to :func:`os.fork`; this has not been necessary since From python-checkins at python.org Mon Aug 4 09:31:50 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 09:31:50 +0200 (CEST) Subject: [Python-checkins] r65480 - python/trunk/Doc/library/select.rst Message-ID: <20080804073150.BCF2A1E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 09:31:50 2008 New Revision: 65480 Log: Clarify the meaning of the select() parameters and sync names with docstring. Modified: python/trunk/Doc/library/select.rst Modified: python/trunk/Doc/library/select.rst ============================================================================== --- python/trunk/Doc/library/select.rst (original) +++ python/trunk/Doc/library/select.rst Mon Aug 4 09:31:50 2008 @@ -58,19 +58,24 @@ .. versionadded:: 2.6 -.. function:: select(iwtd, owtd, ewtd[, timeout]) +.. function:: select(rlist, wlist, xlist[, timeout]) This is a straightforward interface to the Unix :cfunc:`select` system call. The first three arguments are sequences of 'waitable objects': either integers representing file descriptors or objects with a parameterless method - named :meth:`fileno` returning such an integer. The three sequences of - waitable objects are for input, output and 'exceptional conditions', - respectively. Empty sequences are allowed, but acceptance of three empty - sequences is platform-dependent. (It is known to work on Unix but not on - Windows.) The optional *timeout* argument specifies a time-out as a floating - point number in seconds. When the *timeout* argument is omitted the function - blocks until at least one file descriptor is ready. A time-out value of zero - specifies a poll and never blocks. + named :meth:`fileno` returning such an integer: + + * *rlist*: wait until ready for reading + * *wlist*: wait until ready for writing + * *xlist*: wait for an "exceptional condition" (see the manual page for what + your system considers such a condition) + + Empty sequences are allowed, but acceptance of three empty sequences is + platform-dependent. (It is known to work on Unix but not on Windows.) The + optional *timeout* argument specifies a time-out as a floating point number + in seconds. When the *timeout* argument is omitted the function blocks until + at least one file descriptor is ready. A time-out value of zero specifies a + poll and never blocks. The return value is a triple of lists of objects that are ready: subsets of the first three arguments. When the time-out is reached without a file descriptor @@ -90,9 +95,10 @@ .. index:: single: WinSock - File objects on Windows are not acceptable, but sockets are. On Windows, the - underlying :cfunc:`select` function is provided by the WinSock library, and does - not handle file descriptors that don't originate from WinSock. + File objects on Windows are not acceptable, but sockets are. On Windows, + the underlying :cfunc:`select` function is provided by the WinSock + library, and does not handle file descriptors that don't originate from + WinSock. .. _epoll-objects: From python-checkins at python.org Mon Aug 4 09:33:37 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 09:33:37 +0200 (CEST) Subject: [Python-checkins] r65481 - in python/trunk/Python: pystate.c thread.c Message-ID: <20080804073337.DE10E1E4004@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 09:33:37 2008 New Revision: 65481 Log: Adds a sanity check to avoid a *very rare* infinite loop due to a corrupt tls key list data structure in the thread startup path. This change is a companion to r60148 which already successfully dealt with a similar issue on thread shutdown. In particular this loop has been observed happening from this call path: #0 in find_key () #1 in PyThread_set_key_value () #2 in _PyGILState_NoteThreadState () #3 in PyThreadState_New () #4 in t_bootstrap () #5 in pthread_start_thread () I don't know how this happens but it does, *very* rarely. On more than one hardware platform. I have not been able to reproduce it manually. (A flaky mutex implementation on the system in question is one hypothesis). As with r60148, the spinning we managed to observe in the wild was due to a single list element pointing back upon itself. Modified: python/trunk/Python/pystate.c python/trunk/Python/thread.c Modified: python/trunk/Python/pystate.c ============================================================================== --- python/trunk/Python/pystate.c (original) +++ python/trunk/Python/pystate.c Mon Aug 4 09:33:37 2008 @@ -253,6 +253,10 @@ "PyThreadState_Delete: invalid tstate"); if (*p == tstate) break; + /* Sanity check. These states should never happen but if + * they do we must abort. Otherwise we'll end up spinning in + * in a tight loop with the lock held. A similar check is done + * in thread.c find_key(). */ if (*p == prev_p) Py_FatalError( "PyThreadState_Delete: small circular list(!)" Modified: python/trunk/Python/thread.c ============================================================================== --- python/trunk/Python/thread.c (original) +++ python/trunk/Python/thread.c Mon Aug 4 09:33:37 2008 @@ -264,15 +264,25 @@ static struct key * find_key(int key, void *value) { - struct key *p; + struct key *p, *prev_p; long id = PyThread_get_thread_ident(); if (!keymutex) return NULL; PyThread_acquire_lock(keymutex, 1); + prev_p = NULL; for (p = keyhead; p != NULL; p = p->next) { if (p->id == id && p->key == key) goto Done; + /* Sanity check. These states should never happen but if + * they do we must abort. Otherwise we'll end up spinning in + * in a tight loop with the lock held. A similar check is done + * in pystate.c tstate_delete_common(). */ + if (p == prev_p) + Py_FatalError("tls find_key: small circular list(!)"); + prev_p = p; + if (p->next == keyhead) + Py_FatalError("tls find_key: circular list(!)"); } if (value == NULL) { assert(p == NULL); From buildbot at python.org Mon Aug 4 09:49:34 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 07:49:34 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080804074934.EC4651E4004@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1195 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1462, in write self.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 09:52:03 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 07:52:03 +0000 Subject: [Python-checkins] buildbot failure in ia64 Ubuntu 3.0 Message-ID: <20080804075203.E0EBA1E4006@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%20Ubuntu%203.0/builds/363 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ia64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/home/pybot/buildarea/3.0.klose-debian-ia64/build/Lib/io.py", line 1462, in write self.flush() File "/home/pybot/buildarea/3.0.klose-debian-ia64/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/home/pybot/buildarea/3.0.klose-debian-ia64/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 10:48:29 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 08:48:29 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable 3.0 Message-ID: <20080804084829.732931E4004@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%203.0/builds/1304 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: georg.brandl BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/home/pybot/buildarea/3.0.klose-debian-ppc/build/Lib/io.py", line 1462, in write self.flush() File "/home/pybot/buildarea/3.0.klose-debian-ppc/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/home/pybot/buildarea/3.0.klose-debian-ppc/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 11:01:40 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 11:01:40 +0200 (CEST) Subject: [Python-checkins] r65483 - in doctools/branches/0.4.x: doc/markup/code.rst sphinx/directives/code.py sphinx/highlighting.py tests/root/contents.txt tests/root/includes.txt tests/root/literal.inc tests/root/subdir/include.inc tests/root/wrongenc.inc tests/test_build.py tests/util.py Message-ID: <20080804090140.E581C1E4015@bag.python.org> Author: georg.brandl Date: Mon Aug 4 11:01:40 2008 New Revision: 65483 Log: Add an "encoding" option to literalinclude. Add tests for include directives. Added: doctools/branches/0.4.x/tests/root/includes.txt (contents, props changed) doctools/branches/0.4.x/tests/root/literal.inc (contents, props changed) doctools/branches/0.4.x/tests/root/subdir/include.inc (contents, props changed) doctools/branches/0.4.x/tests/root/wrongenc.inc (contents, props changed) Modified: doctools/branches/0.4.x/doc/markup/code.rst doctools/branches/0.4.x/sphinx/directives/code.py doctools/branches/0.4.x/sphinx/highlighting.py doctools/branches/0.4.x/tests/root/contents.txt doctools/branches/0.4.x/tests/test_build.py doctools/branches/0.4.x/tests/util.py Modified: doctools/branches/0.4.x/doc/markup/code.rst ============================================================================== --- doctools/branches/0.4.x/doc/markup/code.rst (original) +++ doctools/branches/0.4.x/doc/markup/code.rst Mon Aug 4 11:01:40 2008 @@ -100,6 +100,15 @@ :language: ruby :linenos: + Include files are assumed to be encoded in UTF-8. If the file has a different + encoding, you can specify it with the ``encoding`` option: + + .. literalinclude:: example.py + :encoding: latin-1 + + .. versionadded:: 0.4.3 + The ``encoding`` option. + .. rubric:: Footnotes Modified: doctools/branches/0.4.x/sphinx/directives/code.py ============================================================================== --- doctools/branches/0.4.x/sphinx/directives/code.py (original) +++ doctools/branches/0.4.x/sphinx/directives/code.py Mon Aug 4 11:01:40 2008 @@ -8,6 +8,7 @@ """ import sys +import codecs from os import path from docutils import nodes @@ -67,13 +68,19 @@ lineno - state_machine.input_offset - 1))) fn = path.normpath(path.join(source_dir, rel_fn)) + encoding = options.get('encoding', 'utf-8') try: - f = open(fn) + f = codecs.open(fn, 'r', encoding) text = f.read() f.close() except (IOError, OSError): retnode = state.document.reporter.warning( 'Include file %r not found or reading it failed' % arguments[0], line=lineno) + except UnicodeError: + retnode = state.document.reporter.warning( + 'Encoding %r used for reading included file %r seems to ' + 'be wrong, try giving an :encoding: option' % + (encoding, arguments[0])) else: retnode = nodes.literal_block(text, text, source=fn) retnode.line = 1 @@ -85,7 +92,8 @@ return [retnode] literalinclude_directive.options = {'linenos': directives.flag, - 'language': directives.unchanged} + 'language': directives.unchanged, + 'encoding': directives.encoding} literalinclude_directive.content = 0 literalinclude_directive.arguments = (1, 0, 0) directives.register_directive('literalinclude', literalinclude_directive) Modified: doctools/branches/0.4.x/sphinx/highlighting.py ============================================================================== --- doctools/branches/0.4.x/sphinx/highlighting.py (original) +++ doctools/branches/0.4.x/sphinx/highlighting.py Mon Aug 4 11:01:40 2008 @@ -128,6 +128,13 @@ if sys.version_info >= (2, 5): src = 'from __future__ import with_statement\n' + src + if isinstance(src, unicode): + # Non-ASCII chars will only occur in string literals + # and comments. If we wanted to give them to the parser + # correctly, we'd have to find out the correct source + # encoding. Since it may not even be given in a snippet, + # just replace all non-ASCII characters. + src = src.encode('ascii', 'replace') try: parser.suite(src) except parsing_exceptions: Modified: doctools/branches/0.4.x/tests/root/contents.txt ============================================================================== --- doctools/branches/0.4.x/tests/root/contents.txt (original) +++ doctools/branches/0.4.x/tests/root/contents.txt Mon Aug 4 11:01:40 2008 @@ -11,6 +11,7 @@ :maxdepth: 2 images + includes Indices and tables ================== @@ -18,4 +19,3 @@ * :ref:`genindex` * :ref:`modindex` * :ref:`search` - Added: doctools/branches/0.4.x/tests/root/includes.txt ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/root/includes.txt Mon Aug 4 11:01:40 2008 @@ -0,0 +1,16 @@ +Test file and literal inclusion +=============================== + +.. include:: subdir/include.inc + +.. literalinclude:: literal.inc + :language: python + +.. should give a warning +.. literalinclude:: wrongenc.inc + +.. should succeed +.. literalinclude:: wrongenc.inc + :encoding: latin-1 +.. include:: wrongenc.inc + :encoding: latin-1 Added: doctools/branches/0.4.x/tests/root/literal.inc ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/root/literal.inc Mon Aug 4 11:01:40 2008 @@ -0,0 +1,4 @@ +# Literally included file using Python highlighting +# -*- coding: utf-8 -*- + +foo = u"Including Unicode characters: ??????" Added: doctools/branches/0.4.x/tests/root/subdir/include.inc ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/root/subdir/include.inc Mon Aug 4 11:01:40 2008 @@ -0,0 +1,5 @@ +.. This file is included by contents.txt. + +.. Paths in included files are relative to the file that + includes them +.. image:: ../root/img.png Added: doctools/branches/0.4.x/tests/root/wrongenc.inc ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/root/wrongenc.inc Mon Aug 4 11:01:40 2008 @@ -0,0 +1,3 @@ +This file is encoded in latin-1 but at first read as utf-8. + +Max Strau? a? in M?nchen eine Leberk?ssemmel. Modified: doctools/branches/0.4.x/tests/test_build.py ============================================================================== --- doctools/branches/0.4.x/tests/test_build.py (original) +++ doctools/branches/0.4.x/tests/test_build.py Mon Aug 4 11:01:40 2008 @@ -25,6 +25,7 @@ ENV_WARNINGS = """\ WARNING: %(root)s/images.txt:9: Image file not readable: foo.png WARNING: %(root)s/images.txt:20: Nonlocal image URI found: http://www.python.org/logo.png +WARNING: %(root)s/includes.txt:: (WARNING/2) Encoding 'utf-8' used for reading included file u'wrongenc.inc' seems to be wrong, try giving an :encoding: option """ HTML_WARNINGS = ENV_WARNINGS + """\ @@ -39,10 +40,16 @@ 'images.html': { ".//img[@src='_images/img.png']": '', ".//img[@src='_images/img1.png']": '', - } + }, + 'includes.html': { + ".//pre/span[@class='s']": u'??????', + ".//pre": u'Max Strau??', + }, } class NslessParser(ET.XMLParser): + """XMLParser that throws away namespaces in tag names.""" + def _fixname(self, key): try: return self._names[key] @@ -63,15 +70,22 @@ if not ET: return - parser = NslessParser() - parser.entity.update(htmlentitydefs.entitydefs) for fname, paths in HTML_XPATH.iteritems(): + parser = NslessParser() + parser.entity.update(htmlentitydefs.entitydefs) etree = ET.parse(app.outdir / fname, parser) for path, text in paths.iteritems(): nodes = list(etree.findall(path)) assert nodes != [] + if not text: + # only check for node presence + continue for node in nodes: - if text: assert text in node.text + if node.text and text in node.text: + break + else: + assert False, ('%r not found in any node matching ' + 'path %s in %s' % (text, path, fname)) @with_testapp(buildername='latex', warning=latex_warnfile) Modified: doctools/branches/0.4.x/tests/util.py ============================================================================== --- doctools/branches/0.4.x/tests/util.py (original) +++ doctools/branches/0.4.x/tests/util.py Mon Aug 4 11:01:40 2008 @@ -130,10 +130,10 @@ def cleanup(self): trees = [self.outdir, self.doctreedir] - if self.made_builddir: - trees.append(self.builddir) - for tree in trees: - shutil.rmtree(tree, True) + #f self.made_builddir: + # trees.append(self.builddir) + #for tree in trees: + # shutil.rmtree(tree, True) def with_testapp(*args, **kwargs): From python-checkins at python.org Mon Aug 4 11:11:18 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 11:11:18 +0200 (CEST) Subject: [Python-checkins] r65484 - doctools/branches/0.4.x/CHANGES Message-ID: <20080804091118.0BA581E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 11:11:17 2008 New Revision: 65484 Log: Add changelog entry. Modified: doctools/branches/0.4.x/CHANGES Modified: doctools/branches/0.4.x/CHANGES ============================================================================== --- doctools/branches/0.4.x/CHANGES (original) +++ doctools/branches/0.4.x/CHANGES Mon Aug 4 11:11:17 2008 @@ -1,6 +1,9 @@ Release 0.4.3 (in development) ============================== +* Fix encoding handling for literal include files: ``literalinclude`` + now has an ``encoding`` option that defaults to UTF-8. + * Fix the handling of non-ASCII characters entered in quickstart. * Fix a crash with nonexisting image URIs. From python-checkins at python.org Mon Aug 4 11:21:58 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 11:21:58 +0200 (CEST) Subject: [Python-checkins] r65485 - doctools/branches/0.4.x/doc/markup/code.rst Message-ID: <20080804092158.8D3B61E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 11:21:58 2008 New Revision: 65485 Log: Fix markup. Modified: doctools/branches/0.4.x/doc/markup/code.rst Modified: doctools/branches/0.4.x/doc/markup/code.rst ============================================================================== --- doctools/branches/0.4.x/doc/markup/code.rst (original) +++ doctools/branches/0.4.x/doc/markup/code.rst Mon Aug 4 11:21:58 2008 @@ -101,7 +101,7 @@ :linenos: Include files are assumed to be encoded in UTF-8. If the file has a different - encoding, you can specify it with the ``encoding`` option: + encoding, you can specify it with the ``encoding`` option:: .. literalinclude:: example.py :encoding: latin-1 From python-checkins at python.org Mon Aug 4 11:54:45 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 11:54:45 +0200 (CEST) Subject: [Python-checkins] r65486 - in doctools/trunk: CHANGES TODO doc/ext/appapi.rst sphinx/application.py sphinx/builder.py sphinx/environment.py sphinx/ext/autodoc.py Message-ID: <20080804095445.ECFBE1E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 11:54:45 2008 New Revision: 65486 Log: Add "env-updated" and "missing-reference" events. Write inventory file as part of the HTML build. Modified: doctools/trunk/CHANGES doctools/trunk/TODO doctools/trunk/doc/ext/appapi.rst doctools/trunk/sphinx/application.py doctools/trunk/sphinx/builder.py doctools/trunk/sphinx/environment.py doctools/trunk/sphinx/ext/autodoc.py Modified: doctools/trunk/CHANGES ============================================================================== --- doctools/trunk/CHANGES (original) +++ doctools/trunk/CHANGES Mon Aug 4 11:54:45 2008 @@ -27,6 +27,8 @@ * sphinx.doc.autodoc has a new event ``autodoc-process-signature`` that allows tuning function signature introspection. +* Added new events: ``env-updated`` and ``missing-reference``. + Release 0.4.2 (Jul 29, 2008) ============================ Modified: doctools/trunk/TODO ============================================================================== --- doctools/trunk/TODO (original) +++ doctools/trunk/TODO Mon Aug 4 11:54:45 2008 @@ -1,14 +1,16 @@ Sphinx TODO =========== -- literal search mode +- RSS generation +- files for downloading - specify node visit functions when adding nodes to app - allow extensions to add static files - decide which static files to include - verbose option - remove redundant
    s in tocs - autoattribute in autodoc -- range and object options for literalinclude +- section, range and object options for literalinclude +- literal search mode - option for compact module index - HTML section numbers? - "seealso" links to external examples, see http://svn.python.org/projects/sandbox/trunk/seealso/ and http://effbot.org/zone/idea-seealso.htm Modified: doctools/trunk/doc/ext/appapi.rst ============================================================================== --- doctools/trunk/doc/ext/appapi.rst (original) +++ doctools/trunk/doc/ext/appapi.rst Mon Aug 4 11:54:45 2008 @@ -144,8 +144,17 @@ .. method:: Sphinx.emit(event, *arguments) - Emit *event* and pass *arguments* to the callback functions. Do not emit - core Sphinx events in extensions! + Emit *event* and pass *arguments* to the callback functions. Return the + return values of all callbacks as a list. Do not emit core Sphinx events + in extensions! + +.. method:: Sphinx.emit_firstresult(event, *arguments) + + Emit *event* and pass *arguments* to the callback functions. Return the + result of the first callback that doesn't return ``None`` (and don't call + the rest of the callbacks). + + .. versionadded:: 0.5 .. exception:: ExtensionError @@ -167,18 +176,43 @@ .. event:: builder-inited (app) - Emitted the builder object has been created. + Emitted when the builder object has been created. It is available as + ``app.builder``. .. event:: doctree-read (app, doctree) Emitted when a doctree has been parsed and read by the environment, and is - about to be pickled. + about to be pickled. The *doctree* can be modified in-place. + +.. event:: missing-reference (app, env, node, contnode) + Emitted when a cross-reference to a Python module or object cannot be + resolved. If the event handler can resolve the reference, it should return a + new docutils node to be inserted in the document tree in place of the node + *node*. Usually this node is a :class:`reference` node containing *contnode* + as a child. + + :param env: The build environment (``app.builder.env``). + :param node: The :class:`pending_xref` node to be resolved. Its attributes + ``reftype``, ``reftarget``, ``modname`` and ``classname`` attributes + determine the type and target of the reference. + :param contnode: The node that carries the text and formatting inside the + future reference and should be a child of the returned reference node. + + .. versionadded:: 0.5 + .. event:: doctree-resolved (app, doctree, docname) Emitted when a doctree has been "resolved" by the environment, that is, all - references and TOCs have been inserted. + references have been resolved and TOCs have been inserted. + +.. event:: env-updated (app, env) + + Emitted when the :meth:`update` method of the build environment has + completed, that is, the environment and all doctrees are now up-to-date. + .. versionadded:: 0.5 + .. event:: page-context (app, pagename, templatename, context, doctree) Emitted when the HTML builder has created a context dictionary to render a Modified: doctools/trunk/sphinx/application.py ============================================================================== --- doctools/trunk/sphinx/application.py (original) +++ doctools/trunk/sphinx/application.py Mon Aug 4 11:54:45 2008 @@ -167,6 +167,12 @@ result.append(callback(self, *args)) return result + def emit_firstresult(self, event, *args): + for result in self.emit(event, *args): + if result is not None: + return result + return None + # registering addon parts def add_builder(self, builder): Modified: doctools/trunk/sphinx/builder.py ============================================================================== --- doctools/trunk/sphinx/builder.py (original) +++ doctools/trunk/sphinx/builder.py Mon Aug 4 11:54:45 2008 @@ -11,6 +11,7 @@ import os import time +import gzip import codecs import shutil import cPickle as pickle @@ -40,6 +41,8 @@ ENV_PICKLE_FILENAME = 'environment.pickle' LAST_BUILD_FILENAME = 'last_build' +INVENTORY_FILENAME = 'inventory.txt.gz' + class Builder(object): """ @@ -153,7 +156,7 @@ return if not self.freshenv: try: - self.info(bold('trying to load pickled env... '), nonl=True) + self.info(bold('loading pickled environment... '), nonl=True) self.env = BuildEnvironment.frompickle(self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME)) self.info('done') @@ -214,7 +217,7 @@ iterator = self.env.update(self.config, self.srcdir, self.doctreedir, self.app) # the first item in the iterator is a summary message self.info(iterator.next()) - for docname in self.status_iterator(iterator, 'reading... ', purple): + for docname in self.status_iterator(iterator, 'reading sources... ', purple): updated_docnames.append(docname) # nothing further to do, the environment has already done the reading for warning in warnings: @@ -224,13 +227,14 @@ if updated_docnames: # save the environment - self.info(bold('pickling the env... '), nonl=True) + self.info(bold('pickling environment... '), nonl=True) self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME)) self.info('done') # global actions - self.info(bold('checking consistency...')) + self.info(bold('checking consistency... '), nonl=True) self.env.check_consistency() + self.info('done') else: if method == 'update' and not docnames: self.info(bold('no targets are out of date.')) @@ -241,7 +245,6 @@ self.write(docnames, updated_docnames, method) # finish (write static files etc.) - self.info(bold('finishing... ')) self.finish() if self.app._warncount: self.info(bold('build succeeded, %s warning%s.' % @@ -266,7 +269,9 @@ docnames.add(tocdocname) docnames.add(self.config.master_doc) + self.info(bold('preparing documents... '), nonl=True) self.prepare_writing(docnames) + self.info('done') # write target files warnings = [] @@ -571,7 +576,7 @@ # copy image files if self.images: - self.info(bold('copying images...'), nonl=1) + self.info(bold('copying images...'), nonl=True) ensuredir(path.join(self.outdir, '_images')) for src, dest in self.images.iteritems(): self.info(' '+src, nonl=1) @@ -580,7 +585,7 @@ self.info() # copy static files - self.info(bold('copying static files...')) + self.info(bold('copying static files... '), nonl=True) ensuredir(path.join(self.outdir, '_static')) staticdirnames = [path.join(path.dirname(__file__), 'static')] + \ [path.join(self.confdir, spath) @@ -605,6 +610,7 @@ f = open(path.join(self.outdir, '_static', 'pygments.css'), 'w') f.write(PygmentsBridge('html', self.config.pygments_style).get_stylesheet()) f.close() + self.info('done') # dump the search index self.handle_finish() @@ -693,13 +699,25 @@ shutil.copyfile(self.env.doc2path(pagename), source_name) def handle_finish(self): - self.info(bold('dumping search index...')) + self.info(bold('dumping search index... '), nonl=True) self.indexer.prune(self.env.all_docs) f = open(path.join(self.outdir, self.searchindex_filename), 'wb') try: self.indexer.dump(f, self.indexer_format) finally: f.close() + self.info('done') + + self.info(bold('dumping object inventory... '), nonl=True) + f = gzip.open(path.join(self.outdir, INVENTORY_FILENAME), 'w') + try: + for modname, info in self.env.modules.iteritems(): + f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) + for refname, (docname, desctype) in self.env.descrefs.iteritems(): + f.write('%s %s %s\n' % (refname, desctype, self.get_target_uri(docname))) + finally: + f.close() + self.info('done') class SerializingHTMLBuilder(StandaloneHTMLBuilder): @@ -974,12 +992,13 @@ shutil.copyfile(path.join(self.confdir, self.config.latex_logo), path.join(self.outdir, logobase)) - self.info(bold('copying TeX support files...')) + self.info(bold('copying TeX support files... '), nonl=True) staticdirname = path.join(path.dirname(__file__), 'texinputs') for filename in os.listdir(staticdirname): if not filename.startswith('.'): shutil.copyfile(path.join(staticdirname, filename), path.join(self.outdir, filename)) + self.info('done') class ChangesBuilder(Builder): Modified: doctools/trunk/sphinx/environment.py ============================================================================== --- doctools/trunk/sphinx/environment.py (original) +++ doctools/trunk/sphinx/environment.py Mon Aug 4 11:54:45 2008 @@ -457,6 +457,9 @@ if not os.access(path.join(self.srcdir, imgsrc), os.R_OK): del self.images[imgsrc] + if app: + app.emit('env-updated', self) + # --------- SINGLE FILE READING -------------------------------------------- @@ -880,6 +883,10 @@ 'meth', 'cfunc', 'cdata', 'ctype', 'cmacro', 'obj')) def resolve_references(self, doctree, fromdocname, builder): + reftarget_roles = set(('token', 'term', 'option')) + # add all custom xref types too + reftarget_roles.update(i[0] for i in additional_xref_types.values()) + for node in doctree.traverse(addnodes.pending_xref): contnode = node[0].deepcopy() newnode = None @@ -887,10 +894,6 @@ typ = node['reftype'] target = node['reftarget'] - reftarget_roles = set(('token', 'term', 'option')) - # add all custom xref types too - reftarget_roles.update(i[0] for i in additional_xref_types.values()) - try: if typ == 'ref': if node['refcaption']: @@ -962,7 +965,12 @@ # because the anchor is generally below the heading which is ugly # but can't be helped easily anchor = '' - if not docname or docname == fromdocname: + if not docname: + newnode = builder.app.emit_firstresult('missing-reference', + self, node, contnode) + if not newnode: + newnode = contnode + elif docname == fromdocname: # don't link to self newnode = contnode else: @@ -983,7 +991,10 @@ name, desc = self.find_desc(modname, clsname, target, typ, searchorder) if not desc: - newnode = contnode + newnode = builder.app.emit_firstresult('missing-reference', + self, node, contnode) + if not newnode: + newnode = contnode else: newnode = nodes.reference('', '') if desc[0] == fromdocname: Modified: doctools/trunk/sphinx/ext/autodoc.py ============================================================================== --- doctools/trunk/sphinx/ext/autodoc.py (original) +++ doctools/trunk/sphinx/ext/autodoc.py Mon Aug 4 11:54:45 2008 @@ -350,11 +350,10 @@ args = None err = e - results = self.env.app.emit('autodoc-process-signature', - what, name, obj, self.options, args, retann) - for result in results: - if result: - args, retann = result + result = self.env.app.emit_firstresult('autodoc-process-signature', what, + name, obj, self.options, args, retann) + if result: + args, retann = result if args is not None: return '%s%s' % (args, retann or '') From nnorwitz at gmail.com Mon Aug 4 11:56:28 2008 From: nnorwitz at gmail.com (Neal Norwitz) Date: Mon, 4 Aug 2008 05:56:28 -0400 Subject: [Python-checkins] Python Regression Test Failures all (1) Message-ID: <20080804095628.GA12756@python.psfb.org> 331 tests OK. 1 test failed: test_smtplib 25 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_epoll test_gl test_imageop test_imgfile test_ioctl test_kqueue test_macos test_macostools test_multiprocessing test_pep277 test_py3kwarn test_scriptpackages test_startfile test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound test_zipfile64 3 skips unexpected on linux2: test_epoll test_multiprocessing test_ioctl test_grammar test_opcodes test_dict test_builtin test_exceptions test_types test_unittest test_doctest test_doctest2 test_MimeWriter test_SimpleHTTPServer test_StringIO test___all__ test___future__ test__locale test_abc test_abstract_numbers test_aepack test_aepack skipped -- No module named aepack test_al test_al skipped -- No module named al test_anydbm test_applesingle test_applesingle skipped -- No module named macostools test_array test_ast test_asynchat test_asyncore test_atexit test_audioop test_augassign test_base64 test_bastion test_bigaddrspace test_bigmem test_binascii test_binhex test_binop test_bisect test_bool test_bsddb test_bsddb185 test_bsddb185 skipped -- No module named bsddb185 test_bsddb3 Sleepycat Software: Berkeley DB 4.1.25: (December 19, 2002) Test path prefix: /tmp/z-test_bsddb3-10301 test_buffer test_bufio test_bytes test_bz2 test_calendar test_call test_capi test_cd test_cd skipped -- No module named cd test_cfgparser test_cgi test_charmapcodec test_cl test_cl skipped -- No module named cl test_class test_cmath test_cmd test_cmd_line test_cmd_line_script test_code test_codeccallbacks test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecmaps_cn test_codecmaps_hk test_codecmaps_jp test_codecmaps_kr test_codecmaps_tw test_codecs test_codeop test_coding test_coercion test_collections test_colorsys test_commands test_compare test_compile test_compiler testCompileLibrary still working, be patient... test_complex test_complex_args test_contains test_contextlib test_cookie test_cookielib test_copy test_copy_reg test_cpickle test_cprofile test_crypt test_csv test_ctypes test_datetime test_dbm test_decimal test_decorators test_defaultdict test_deque test_descr test_descrtut test_difflib test_dircache test_dis test_distutils test_dl test_docxmlrpc test_dumbdbm test_dummy_thread test_dummy_threading test_email test_email_codecs test_email_renamed test_enumerate test_eof test_epoll test_epoll skipped -- kernel doesn't support epoll() test_errno test_exception_variations test_extcall test_fcntl test_file test_filecmp test_fileinput test_fileio test_float test_fnmatch test_fork1 test_format test_fpformat test_fractions test_frozen test_ftplib test_funcattrs test_functools test_future test_future3 test_future4 test_future_builtins test_gc test_gdbm test_generators test_genericpath test_genexps test_getargs test_getargs2 test_getopt test_gettext test_gl test_gl skipped -- No module named gl test_glob test_global test_grp test_gzip test_hash test_hashlib test_heapq test_hmac test_hotshot test_htmllib test_htmlparser test_httplib test_httpservers [12446 refs] [12446 refs] [20833 refs] test_imageop test_imageop skipped -- No module named imgfile test_imaplib test_imgfile test_imgfile skipped -- No module named imgfile test_imp test_import test_importhooks test_index test_inspect test_int test_int_literal test_io test_ioctl test_ioctl skipped -- Unable to open /dev/tty test_isinstance test_iter test_iterlen test_itertools test_json test_kqueue test_kqueue skipped -- test works only on BSD test_largefile test_lib2to3 test_list test_locale test_logging test_long test_long_future test_longexp test_macos test_macos skipped -- No module named MacOS test_macostools test_macostools skipped -- No module named macostools test_macpath test_mailbox test_marshal test_math test_md5 test_memoryio test_mhlib test_mimetools test_mimetypes test_minidom test_mmap test_module test_modulefinder test_multibytecodec test_multibytecodec_support test_multifile test_multiprocessing test_multiprocessing skipped -- OSError raises on RLock creation, see issue 3111! test_mutants test_mutex test_netrc test_new test_nis test_normalization test_ntpath test_old_mailbox test_openpty test_operator test_optparse test_os test_parser Expecting 's_push: parser stack overflow' in next line s_push: parser stack overflow test_peepholer test_pep247 test_pep263 test_pep277 test_pep277 skipped -- test works only on NT+ test_pep292 test_pep352 test_pickle test_pickletools test_pipes test_pkg test_pkgimport test_pkgutil test_platform test_plistlib test_poll test_popen [12451 refs] [12451 refs] [12451 refs] test_popen2 test_poplib test_posix test_posixpath test_pow test_pprint test_print test_profile test_profilehooks test_property test_pstats test_pty test_pwd test_py3kwarn test_py3kwarn skipped -- test.test_py3kwarn must be run with the -3 flag test_pyclbr test_pydoc [16982 refs] test_pyexpat test_queue test_quopri [14965 refs] [14965 refs] test_random test_re test_repr test_resource test_rfc822 test_richcmp test_robotparser test_runpy test_sax test_scope test_scriptpackages test_scriptpackages skipped -- No module named aetools test_select test_set test_sets test_sgmllib test_sha test_shelve test_shlex test_shutil test_signal test_site [12446 refs] [12446 refs] [12449 refs] [12446 refs] test_slice test_smtplib test test_smtplib produced unexpected output: ********************************************************************** error: uncaptured python exception, closing channel (:[Errno 9] Bad file descriptor [/tmp/python-test/local/lib/python2.6/asyncore.py|readwrite|101] [/tmp/python-test/local/lib/python2.6/asyncore.py|handle_write_event|427] [|getsockopt|1] [/tmp/python-test/local/lib/python2.6/socket.py|_dummy|165]) ********************************************************************** test_socket test_socketserver test_softspace test_sort test_sqlite test_ssl test_startfile test_startfile skipped -- cannot import name startfile test_str test_strftime test_string test_stringprep test_strop test_strptime test_struct test_structmembers test_structseq test_subprocess [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [14346 refs] [12664 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] [12446 refs] . [12446 refs] [12446 refs] this bit of output is from a test of stdout in a different process ... [12446 refs] [12446 refs] [12664 refs] test_sunaudiodev test_sunaudiodev skipped -- No module named sunaudiodev test_sundry test_symtable test_syntax test_sys [12446 refs] [12446 refs] [12675 refs] [12469 refs] test_tarfile test_tcl test_tcl skipped -- No module named _tkinter test_telnetlib test_tempfile [12449 refs] test_textwrap test_thread test_threaded_import test_threadedtempfile test_threading [15674 refs] [16373 refs] [15344 refs] [15344 refs] [15344 refs] [15344 refs] test_threading_local test_threadsignals test_time test_timeout test_tokenize test_trace test_traceback test_transformer test_tuple test_typechecks test_ucn test_unary test_undocumented_details test_unicode test_unicode_file test_unicode_file skipped -- No Unicode filesystem semantics on this platform. test_unicodedata test_univnewlines test_unpack test_urllib test_urllib2 test_urllib2_localnet test_urllib2net test_urllibnet test_urlparse test_userdict test_userlist test_userstring test_uu test_uuid WARNING: uuid.getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._ifconfig_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. WARNING: uuid._unixdll_getnode is unreliable on many platforms. It is disabled until the code and/or test can be fixed properly. test_wait3 test_wait4 test_warnings test_wave test_weakref test_whichdb test_winreg test_winreg skipped -- No module named _winreg test_winsound test_winsound skipped -- No module named winsound test_with test_wsgiref test_xdrlib test_xml_etree test_xml_etree_c test_xmllib test_xmlrpc test_xpickle test_xrange test_zipfile test_zipfile64 test_zipfile64 skipped -- test requires loads of disk-space bytes and a long time to run test_zipimport test_zlib 331 tests OK. 1 test failed: test_smtplib 25 tests skipped: test_aepack test_al test_applesingle test_bsddb185 test_cd test_cl test_epoll test_gl test_imageop test_imgfile test_ioctl test_kqueue test_macos test_macostools test_multiprocessing test_pep277 test_py3kwarn test_scriptpackages test_startfile test_sunaudiodev test_tcl test_unicode_file test_winreg test_winsound test_zipfile64 3 skips unexpected on linux2: test_epoll test_multiprocessing test_ioctl [658669 refs] From python-checkins at python.org Mon Aug 4 14:41:00 2008 From: python-checkins at python.org (nick.coghlan) Date: Mon, 4 Aug 2008 14:41:00 +0200 (CEST) Subject: [Python-checkins] r65487 - python/trunk/Doc/reference/datamodel.rst Message-ID: <20080804124100.1C9E51E4012@bag.python.org> Author: nick.coghlan Date: Mon Aug 4 14:40:59 2008 New Revision: 65487 Log: Issue 643841: better documentation of the special method lookup process, especially for new-style classes. Also removes the warnings about not being authoritative for new-style classes - the language reference actually covers those fairly well now (albeit in a fashion that isn't always particularly easy to follow). Modified: python/trunk/Doc/reference/datamodel.rst Modified: python/trunk/Doc/reference/datamodel.rst ============================================================================== --- python/trunk/Doc/reference/datamodel.rst (original) +++ python/trunk/Doc/reference/datamodel.rst Mon Aug 4 14:40:59 2008 @@ -742,12 +742,23 @@ of the shared library file. Classes - Class objects are created by class definitions (see section :ref:`class`). A - class has a namespace implemented by a dictionary object. Class attribute - references are translated to lookups in this dictionary, e.g., ``C.x`` is - translated to ``C.__dict__["x"]``. When the attribute name is not found - there, the attribute search continues in the base classes. The search is - depth-first, left-to-right in the order of occurrence in the base class list. + Both class types (new-style classes) and class objects (old-style/classic + classes) are typically created by class definitions (see section + :ref:`class`). A class has a namespace implemented by a dictionary object. + Class attribute references are translated to lookups in this dictionary, e.g., + ``C.x`` is translated to ``C.__dict__["x"]`` (although for new-style classes + in particular there are a number of hooks which allow for other means of + locating attributes). When the attribute name is not found there, the + attribute search continues in the base classes. For old-style classes, the + search is depth-first, left-to-right in the order of occurrence in the base + class list. New-style classes use the more complex C3 method resolution + order which behaves correctly even in the presence of 'diamond' + inheritance structures where there are multiple inheritance paths + leading back to a common ancestor. Additional details on the C3 MRO used by + new-style classes can be found in the documentation accompanying the + 2.3 release at http://www.python.org/download/releases/2.3/mro/. + + .. XXX: Could we add that MRO doc as an appendix to the language ref? .. index:: object: class @@ -768,7 +779,7 @@ static method object, it is transformed into the object wrapped by the static method object. See section :ref:`descriptors` for another way in which attributes retrieved from a class may differ from those actually contained in - its :attr:`__dict__`. + its :attr:`__dict__` (note that only new-style classes support descriptors). .. index:: triple: class; attribute; assignment @@ -1075,7 +1086,7 @@ New-style and classic classes ============================= -Classes and instances come in two flavors: old-style or classic, and new-style. +Classes and instances come in two flavors: old-style (or classic) and new-style. Up to Python 2.1, old-style classes were the only flavour available to the user. The concept of (old-style) class is unrelated to the concept of type: if *x* is @@ -1086,10 +1097,12 @@ New-style classes were introduced in Python 2.2 to unify classes and types. A new-style class is neither more nor less than a user-defined type. If *x* is an -instance of a new-style class, then ``type(x)`` is the same as ``x.__class__``. +instance of a new-style class, then ``type(x)`` is typically the same as +``x.__class__`` (although this is not guaranteed - a new-style class instance is +permitted to override the value returned for ``x.__class__``). The major motivation for introducing new-style classes is to provide a unified -object model with a full meta-model. It also has a number of immediate +object model with a full meta-model. It also has a number of practical benefits, like the ability to subclass most built-in types, or the introduction of "descriptors", which enable computed properties. @@ -1103,16 +1116,18 @@ implemented before for compatibility concerns, like the method resolution order in case of multiple inheritance. -This manual is not up-to-date with respect to new-style classes. For now, -please see http://www.python.org/doc/newstyle/ for more information. +While this manual aims to provide comprehensive coverage of Python's class +mechanics, it may still be lacking in some areas when it comes to its coverage +of new-style classes. Please see http://www.python.org/doc/newstyle/ for +sources of additional information. .. index:: single: class; new-style single: class; classic single: class; old-style -The plan is to eventually drop old-style classes, leaving only the semantics of -new-style classes. This change will probably only be feasible in Python 3.0. +Old-style classes are removed in Python 3.0, leaving only the semantics of +new-style classes. .. _specialnames: @@ -1129,24 +1144,11 @@ with special names. This is Python's approach to :dfn:`operator overloading`, allowing classes to define their own behavior with respect to language operators. For instance, if a class defines a method named :meth:`__getitem__`, -and ``x`` is an instance of this class, then ``x[i]`` is equivalent [#]_ to -``x.__getitem__(i)``. Except where mentioned, attempts to execute an operation -raise an exception when no appropriate method is defined. - -For new-style classes, special methods are only guaranteed to work if defined in -an object's class, not in the object's instance dictionary. That explains why -this won't work:: - - >>> class C: - ... pass - ... - >>> c = C() - >>> c.__len__ = lambda: 5 - >>> len(c) - Traceback (most recent call last): - File "", line 1, in - TypeError: object of type 'C' has no len() - +and ``x`` is an instance of this class, then ``x[i]`` is roughly equivalent +to ``x.__getitem__(i)`` for old-style classes and ``type(x).__getitem__(x, i)`` +for new-style classes. Except where mentioned, attempts to execute an +operation raise an exception when no appropriate method is defined (typically +:exc:`AttributeError` or :exc:`TypeError`). When implementing a class that emulates any built-in type, it is important that the emulation only be implemented to the degree that it makes sense for the @@ -1479,6 +1481,12 @@ method with the same name to access any attributes it needs, for example, ``object.__getattribute__(self, name)``. + .. note:: + + This method may still be bypassed when looking up special methods as the + result of implicit invocation via language syntax or builtin functions. + See :ref:`new-style-special-lookup`. + .. _descriptors: @@ -2274,19 +2282,115 @@ The specification, background, and examples for the Python :keyword:`with` statement. -.. rubric:: Footnotes -.. [#] Since Python 2.2, a gradual merging of types and classes has been started that - makes this and a few other assertions made in this manual not 100% accurate and - complete: for example, it *is* now possible in some cases to change an object's - type, under certain controlled conditions. Until this manual undergoes - extensive revision, it must now be taken as authoritative only regarding - "classic classes", that are still the default, for compatibility purposes, in - Python 2.2 and 2.3. For more information, see - http://www.python.org/doc/newstyle/. +.. _old-style-special-lookup: + +Special method lookup for old-style classes +------------------------------------------- + +For old-style classes, special methods are always looked up in exactly the +same way as any other method or attribute. This is the case regardless of +whether the method is being looked up explicitly as in ``x.__getitem__(i)`` +or implicitly as in ``x[i]``. + +This behaviour means that special methods may exhibit different behaviour +for different instances of a single old-style class if the appropriate +special attributes are set differently:: + + >>> class C: + ... pass + ... + >>> c1 = C() + >>> c2 = C() + >>> c1.__len__ = lambda: 5 + >>> c2.__len__ = lambda: 9 + >>> len(c1) + 5 + >>> len(c2) + 9 + + +.. _new-style-special-lookup: + +Special method lookup for new-style classes +------------------------------------------- + +For new-style classes, implicit invocations of special methods are only guaranteed +to work correctly if defined on an object's type, not in the object's instance +dictionary. That behaviour is the reason why the following code raises an +exception (unlike the equivalent example with old-style classes):: + + >>> class C(object): + ... pass + ... + >>> c = C() + >>> c.__len__ = lambda: 5 + >>> len(c) + Traceback (most recent call last): + File "", line 1, in + TypeError: object of type 'C' has no len() + +The rationale behind this behaviour lies with a number of special methods such +as :meth:`__hash__` and :meth:`__repr__` that are implemented by all objects, +including type objects. If the implicit lookup of these methods used the +conventional lookup process, they would fail when invoked on the type object +itself:: + + >>> 1 .__hash__() == hash(1) + True + >>> int.__hash__() == hash(int) + Traceback (most recent call last): + File "", line 1, in + TypeError: descriptor '__hash__' of 'int' object needs an argument + +Incorrectly attempting to invoke an unbound method of a class in this way is +sometimes referred to as 'metaclass confusion', and is avoided by bypassing +the instance when looking up special methods:: + + >>> type(1).__hash__(1) == hash(1) + True + >>> type(int).__hash__(int) == hash(int) + True + +In addition to bypassing any instance attributes in the interest of +correctness, implicit special method lookup may also bypass the +:meth:`__getattribute__` method even of the object's metaclass:: + + >>> class Meta(type): + ... def __getattribute__(*args): + ... print "Metaclass getattribute invoked" + ... return type.__getattribute__(*args) + ... + >>> class C(object): + ... __metaclass__ = Meta + ... def __len__(self): + ... return 10 + ... def __getattribute__(*args): + ... print "Class getattribute invoked" + ... return object.__getattribute__(*args) + ... + >>> c = C() + >>> c.__len__() # Explicit lookup via instance + Class getattribute invoked + 10 + >>> type(c).__len__(c) # Explicit lookup via type + Metaclass getattribute invoked + 10 + >>> len(c) # Implicit lookup + 10 + +Bypassing the :meth:`__getattribute__` machinery in this fashion +provides significant scope for speed optimisations within the +interpreter, at the cost of some flexibility in the handling of +special methods (the special method *must* be set on the class +object itself in order to be consistently invoked by the interpreter). + + +.. rubric:: Footnotes -.. [#] This, and other statements, are only roughly true for instances of new-style - classes. +.. [#] It *is* possible in some cases to change an object's type, under certain + controlled conditions. It generally isn't a good idea though, since it can + lead to some very strange behaviour if it is handled incorrectly. .. [#] A descriptor can define any combination of :meth:`__get__`, :meth:`__set__` and :meth:`__delete__`. If it does not define :meth:`__get__`, From python-checkins at python.org Mon Aug 4 14:47:17 2008 From: python-checkins at python.org (nick.coghlan) Date: Mon, 4 Aug 2008 14:47:17 +0200 (CEST) Subject: [Python-checkins] r65488 - python/trunk/Misc/NEWS Message-ID: <20080804124717.C59181E4006@bag.python.org> Author: nick.coghlan Date: Mon Aug 4 14:47:17 2008 New Revision: 65488 Log: Add missing NEWS entry for r65487 Modified: python/trunk/Misc/NEWS Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Mon Aug 4 14:47:17 2008 @@ -80,6 +80,14 @@ - Generate the PatternGrammar pickle during "make install". +Documentation +------------- + +- Issue #643841: The language reference now provides more detailed + coverage of the lookup process for special methods. The disclaimers + regarding lack of coverage of new-style classes have also been + removed, since the coverage is now fairly reasonable. + What's New in Python 2.6 beta 2? ================================ From python-checkins at python.org Mon Aug 4 14:54:16 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 14:54:16 +0200 (CEST) Subject: [Python-checkins] r65489 - doctools/trunk/sphinx/ext/autodoc.py Message-ID: <20080804125416.199E41E4006@bag.python.org> Author: georg.brandl Date: Mon Aug 4 14:54:15 2008 New Revision: 65489 Log: #3498: fix unbound local. Modified: doctools/trunk/sphinx/ext/autodoc.py Modified: doctools/trunk/sphinx/ext/autodoc.py ============================================================================== --- doctools/trunk/sphinx/ext/autodoc.py (original) +++ doctools/trunk/sphinx/ext/autodoc.py Mon Aug 4 14:54:15 2008 @@ -310,7 +310,7 @@ if hasattr(self.env, 'autodoc_current_class'): mod_cls = self.env.autodoc_current_class # ... or from a class directive - if not mod_cls: + else: mod_cls = self.env.currclass mod, cls = rpartition(mod_cls, '.') # if the module name is still missing, get it like above From buildbot at python.org Mon Aug 4 15:18:25 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 13:18:25 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080804131825.443D51E4006@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/884 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: nick.coghlan BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1462, in write self.flush() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 15:22:37 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 13:22:37 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080804132237.E95F71E4006@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1197 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: nick.coghlan BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1462, in write self.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 16:40:10 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 14:40:10 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080804144010.D70571E4004@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/104 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: nick.coghlan BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "", line 1, in File "C:\buildbot\3.0.heller-windows-amd64\build\lib\multiprocessing\forking.py", line 297, in main self = load(from_parent) File "C:\buildbot\3.0.heller-windows-amd64\build\lib\pickle.py", line 1325, in load return Unpickler(file, encoding=encoding, errors=errors).load() EOFError Traceback (most recent call last): File "../lib/test/regrtest.py", line 1197, in main() File "../lib/test/regrtest.py", line 401, in main sys.stdout.flush() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\io.py", line 1427, in flush self.buffer.flush() File "C:\buildbot\3.0.heller-windows-amd64\build\lib\io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 17:15:11 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 15:15:11 +0000 Subject: [Python-checkins] buildbot failure in OS X x86 3.0 Message-ID: <20080804151511.D6A8E1E4004@bag.python.org> The Buildbot has detected a new failure of OS X x86 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/OS%20X%20x86%203.0/builds/130 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: noller-osx86 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: antoine.pitrou,brett.cannon,georg.brandl,nick.coghlan BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 17:57:27 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 17:57:27 +0200 (CEST) Subject: [Python-checkins] r65491 - doctools/trunk/sphinx/application.py Message-ID: <20080804155727.3689E1E4006@bag.python.org> Author: georg.brandl Date: Mon Aug 4 17:57:26 2008 New Revision: 65491 Log: Add missing events. Modified: doctools/trunk/sphinx/application.py Modified: doctools/trunk/sphinx/application.py ============================================================================== --- doctools/trunk/sphinx/application.py (original) +++ doctools/trunk/sphinx/application.py Mon Aug 4 17:57:26 2008 @@ -48,8 +48,10 @@ # List of all known core events. Maps name to arguments description. events = { 'builder-inited': '', - 'doctree-read' : 'the doctree before being pickled', - 'doctree-resolved' : 'doctree, docname', + 'doctree-read': 'the doctree before being pickled', + 'missing-reference': 'env, node, contnode', + 'doctree-resolved': 'doctree, docname', + 'env-updated': 'env', 'html-page-context': 'pagename, context, doctree or None', } From python-checkins at python.org Mon Aug 4 17:57:34 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 17:57:34 +0200 (CEST) Subject: [Python-checkins] r65492 - doctools/trunk/sphinx/ext/autodoc.py Message-ID: <20080804155734.6D9D91E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 17:57:34 2008 New Revision: 65492 Log: Fix previous fix. Modified: doctools/trunk/sphinx/ext/autodoc.py Modified: doctools/trunk/sphinx/ext/autodoc.py ============================================================================== --- doctools/trunk/sphinx/ext/autodoc.py (original) +++ doctools/trunk/sphinx/ext/autodoc.py Mon Aug 4 17:57:34 2008 @@ -305,12 +305,13 @@ if path: mod_cls = path.rstrip('.') else: + mod_cls = None # if documenting a class-level object without path, there must be a # current class, either from a parent auto directive ... if hasattr(self.env, 'autodoc_current_class'): mod_cls = self.env.autodoc_current_class # ... or from a class directive - else: + if mod_cls is None: mod_cls = self.env.currclass mod, cls = rpartition(mod_cls, '.') # if the module name is still missing, get it like above From python-checkins at python.org Mon Aug 4 17:57:45 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 17:57:45 +0200 (CEST) Subject: [Python-checkins] r65493 - doctools/trunk/sphinx/builder.py Message-ID: <20080804155745.6EA761E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 17:57:45 2008 New Revision: 65493 Log: Little extension to the inventory protocol. Modified: doctools/trunk/sphinx/builder.py Modified: doctools/trunk/sphinx/builder.py ============================================================================== --- doctools/trunk/sphinx/builder.py (original) +++ doctools/trunk/sphinx/builder.py Mon Aug 4 17:57:45 2008 @@ -11,7 +11,6 @@ import os import time -import gzip import codecs import shutil import cPickle as pickle @@ -41,7 +40,7 @@ ENV_PICKLE_FILENAME = 'environment.pickle' LAST_BUILD_FILENAME = 'last_build' -INVENTORY_FILENAME = 'inventory.txt.gz' +INVENTORY_FILENAME = 'inventory.txt' class Builder(object): @@ -709,8 +708,11 @@ self.info('done') self.info(bold('dumping object inventory... '), nonl=True) - f = gzip.open(path.join(self.outdir, INVENTORY_FILENAME), 'w') + f = open(path.join(self.outdir, INVENTORY_FILENAME), 'w') try: + f.write('# Sphinx inventory version 1\n') + f.write('# Project: %s\n' % self.config.project.encode('utf-8')) + f.write('# Version: %s\n' % self.config.version) for modname, info in self.env.modules.iteritems(): f.write('%s mod %s\n' % (modname, self.get_target_uri(info[0]))) for refname, (docname, desctype) in self.env.descrefs.iteritems(): From python-checkins at python.org Mon Aug 4 18:35:00 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 18:35:00 +0200 (CEST) Subject: [Python-checkins] r65494 - doctools/branches/0.4.x/sphinx/builder.py Message-ID: <20080804163500.257871E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 18:34:59 2008 New Revision: 65494 Log: Correctly use HTML file suffix in templates. Modified: doctools/branches/0.4.x/sphinx/builder.py Modified: doctools/branches/0.4.x/sphinx/builder.py ============================================================================== --- doctools/branches/0.4.x/sphinx/builder.py (original) +++ doctools/branches/0.4.x/sphinx/builder.py Mon Aug 4 18:34:59 2008 @@ -380,7 +380,7 @@ docstitle = self.config.html_title, shorttitle = self.config.html_short_title, show_sphinx = self.config.html_show_sphinx, - file_suffix = self.config.html_file_suffix, + file_suffix = self.out_suffix, rellinks = rellinks, builder = self.name, parents = [], From python-checkins at python.org Mon Aug 4 18:37:40 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 18:37:40 +0200 (CEST) Subject: [Python-checkins] r65495 - doctools/trunk/sphinx/builder.py Message-ID: <20080804163740.754551E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 18:37:40 2008 New Revision: 65495 Log: Show Sphinx version in footer. Modified: doctools/trunk/sphinx/builder.py Modified: doctools/trunk/sphinx/builder.py ============================================================================== --- doctools/trunk/sphinx/builder.py (original) +++ doctools/trunk/sphinx/builder.py Mon Aug 4 18:37:40 2008 @@ -24,7 +24,7 @@ from docutils.frontend import OptionParser from docutils.readers.doctree import Reader as DoctreeReader -from sphinx import addnodes +from sphinx import addnodes, __version__ from sphinx.util import ensuredir, relative_uri, SEP, os_path, json from sphinx.htmlhelp import build_hhx from sphinx.htmlwriter import HTMLWriter, HTMLTranslator, SmartyPantsHTMLTranslator @@ -384,12 +384,13 @@ docstitle = self.config.html_title, shorttitle = self.config.html_short_title, show_sphinx = self.config.html_show_sphinx, - file_suffix = self.config.html_file_suffix, + file_suffix = self.out_suffix, + sphinx_version = __version__, rellinks = rellinks, builder = self.name, parents = [], logo = logo, - favicon = favicon + favicon = favicon, ) def get_doc_context(self, docname, body): From python-checkins at python.org Mon Aug 4 18:38:04 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 18:38:04 +0200 (CEST) Subject: [Python-checkins] r65496 - doctools/trunk/sphinx/templates/layout.html Message-ID: <20080804163804.845F61E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 18:38:04 2008 New Revision: 65496 Log: Show Sphinx version in footer. Modified: doctools/trunk/sphinx/templates/layout.html Modified: doctools/trunk/sphinx/templates/layout.html ============================================================================== --- doctools/trunk/sphinx/templates/layout.html (original) +++ doctools/trunk/sphinx/templates/layout.html Mon Aug 4 18:38:04 2008 @@ -181,7 +181,7 @@ Last updated on {{ last_updated }}. {%- endif %} {%- if show_sphinx %} - Created using Sphinx. + Created using Sphinx {{ sphinx_version }}. {%- endif %} {%- endblock %} From python-checkins at python.org Mon Aug 4 19:01:16 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 19:01:16 +0200 (CEST) Subject: [Python-checkins] r65497 - in doctools/trunk: EXAMPLES Makefile doc/markup/code.rst ez_setup.py sphinx/builder.py sphinx/directives/code.py sphinx/environment.py sphinx/highlighting.py sphinx/quickstart.py sphinx/util/__init__.py tests/etree13 tests/root/conf.py tests/root/contents.txt tests/root/images.txt tests/root/img.gif tests/root/img.pdf tests/root/img.png tests/root/includes.txt tests/root/literal.inc tests/root/subdir tests/root/wrongenc.inc tests/test_build.py tests/test_config.py tests/test_env.py tests/test_markup.py tests/test_quickstart.py tests/util.py Message-ID: <20080804170116.039FA1E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 19:01:15 2008 New Revision: 65497 Log: Merged revisions 65283,65303,65316-65317,65372-65375,65377,65380,65483-65485,65494 via svnmerge from svn+ssh://pythondev at svn.python.org/doctools/branches/0.4.x ........ r65283 | georg.brandl | 2008-07-29 10:07:26 +0000 (Tue, 29 Jul 2008) | 2 lines Update ez_setup.py. ........ r65303 | benjamin.peterson | 2008-07-30 12:35:34 +0000 (Wed, 30 Jul 2008) | 1 line add a with_testapp decorator for test functions that passes the TestApp instance in a cleans up after it ........ r65316 | benjamin.peterson | 2008-07-30 23:12:07 +0000 (Wed, 30 Jul 2008) | 1 line make the app for test_markup global to the module ........ r65317 | benjamin.peterson | 2008-07-30 23:31:29 +0000 (Wed, 30 Jul 2008) | 1 line make TestApp.cleanup more aggressive ........ r65372 | georg.brandl | 2008-08-01 19:11:22 +0000 (Fri, 01 Aug 2008) | 2 lines Add more tests, fix a few bugs in image handling. ........ r65373 | georg.brandl | 2008-08-01 19:28:33 +0000 (Fri, 01 Aug 2008) | 2 lines Fix oversight. ........ r65374 | benjamin.peterson | 2008-08-01 19:36:32 +0000 (Fri, 01 Aug 2008) | 1 line fix one broken test ........ r65375 | georg.brandl | 2008-08-01 19:41:11 +0000 (Fri, 01 Aug 2008) | 2 lines Fix the handling of non-ASCII input in quickstart. ........ r65377 | georg.brandl | 2008-08-01 19:48:24 +0000 (Fri, 01 Aug 2008) | 2 lines Allow REs in markup checks. ........ r65380 | georg.brandl | 2008-08-01 20:31:18 +0000 (Fri, 01 Aug 2008) | 2 lines Don't rely on mtimes being different for changed files. ........ r65483 | georg.brandl | 2008-08-04 09:01:40 +0000 (Mon, 04 Aug 2008) | 4 lines Add an "encoding" option to literalinclude. Add tests for include directives. ........ r65484 | georg.brandl | 2008-08-04 09:11:17 +0000 (Mon, 04 Aug 2008) | 2 lines Add changelog entry. ........ r65485 | georg.brandl | 2008-08-04 09:21:58 +0000 (Mon, 04 Aug 2008) | 2 lines Fix markup. ........ r65494 | georg.brandl | 2008-08-04 16:34:59 +0000 (Mon, 04 Aug 2008) | 2 lines Correctly use HTML file suffix in templates. ........ Added: doctools/trunk/tests/etree13/ - copied from r65494, /doctools/branches/0.4.x/tests/etree13/ doctools/trunk/tests/root/images.txt - copied unchanged from r65494, /doctools/branches/0.4.x/tests/root/images.txt doctools/trunk/tests/root/img.gif - copied unchanged from r65494, /doctools/branches/0.4.x/tests/root/img.gif doctools/trunk/tests/root/img.pdf - copied unchanged from r65494, /doctools/branches/0.4.x/tests/root/img.pdf doctools/trunk/tests/root/img.png - copied unchanged from r65494, /doctools/branches/0.4.x/tests/root/img.png doctools/trunk/tests/root/includes.txt - copied unchanged from r65494, /doctools/branches/0.4.x/tests/root/includes.txt doctools/trunk/tests/root/literal.inc - copied unchanged from r65494, /doctools/branches/0.4.x/tests/root/literal.inc doctools/trunk/tests/root/subdir/ - copied from r65494, /doctools/branches/0.4.x/tests/root/subdir/ doctools/trunk/tests/root/wrongenc.inc - copied unchanged from r65494, /doctools/branches/0.4.x/tests/root/wrongenc.inc doctools/trunk/tests/test_build.py - copied unchanged from r65494, /doctools/branches/0.4.x/tests/test_build.py doctools/trunk/tests/test_env.py - copied unchanged from r65494, /doctools/branches/0.4.x/tests/test_env.py Modified: doctools/trunk/ (props changed) doctools/trunk/EXAMPLES doctools/trunk/Makefile doctools/trunk/doc/markup/code.rst doctools/trunk/ez_setup.py doctools/trunk/sphinx/builder.py doctools/trunk/sphinx/directives/code.py doctools/trunk/sphinx/environment.py doctools/trunk/sphinx/highlighting.py doctools/trunk/sphinx/quickstart.py doctools/trunk/sphinx/util/__init__.py doctools/trunk/tests/root/conf.py doctools/trunk/tests/root/contents.txt doctools/trunk/tests/test_config.py doctools/trunk/tests/test_markup.py doctools/trunk/tests/test_quickstart.py doctools/trunk/tests/util.py Modified: doctools/trunk/EXAMPLES ============================================================================== --- doctools/trunk/EXAMPLES (original) +++ doctools/trunk/EXAMPLES Mon Aug 4 19:01:15 2008 @@ -20,6 +20,7 @@ * Py on Windows: http://timgolden.me.uk/python-on-windows/ * mpmath: http://mpmath.googlecode.com/svn/trunk/doc/build/index.html * Zope 3: e.g. http://docs.carduner.net/z3c-tutorial/ +* Glashammer: http://glashammer.welterde.de/ * SymPy: http://docs.sympy.org/ * Grok (upcoming) * Django (upcoming) Modified: doctools/trunk/Makefile ============================================================================== --- doctools/trunk/Makefile (original) +++ doctools/trunk/Makefile Mon Aug 4 19:01:15 2008 @@ -27,4 +27,4 @@ @$(PYTHON) utils/reindent.py -r -B . test: - @cd tests; $(PYTHON) run.py -d + @cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST) Modified: doctools/trunk/doc/markup/code.rst ============================================================================== --- doctools/trunk/doc/markup/code.rst (original) +++ doctools/trunk/doc/markup/code.rst Mon Aug 4 19:01:15 2008 @@ -100,6 +100,15 @@ :language: ruby :linenos: + Include files are assumed to be encoded in UTF-8. If the file has a different + encoding, you can specify it with the ``encoding`` option:: + + .. literalinclude:: example.py + :encoding: latin-1 + + .. versionadded:: 0.4.3 + The ``encoding`` option. + .. rubric:: Footnotes Modified: doctools/trunk/ez_setup.py ============================================================================== --- doctools/trunk/ez_setup.py (original) +++ doctools/trunk/ez_setup.py Mon Aug 4 19:01:15 2008 @@ -14,8 +14,8 @@ This file can also be run as a script to install or upgrade setuptools. """ import sys -DEFAULT_VERSION = "0.6c5" -DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3] +DEFAULT_VERSION = "0.6c8" +DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] md5_data = { 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', @@ -39,6 +39,15 @@ 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', + 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', + 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', + 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', + 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', + 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', + 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', + 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', + 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', + 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', } import sys, os @@ -71,31 +80,31 @@ this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ - try: - import setuptools - if setuptools.__version__ == '0.0.1': - print >>sys.stderr, ( - "You have an obsolete version of setuptools installed. Please\n" - "remove it from your system entirely before rerunning this script." - ) - sys.exit(2) - except ImportError: + was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules + def do_download(): egg = download_setuptools(version, download_base, to_dir, download_delay) sys.path.insert(0, egg) import setuptools; setuptools.bootstrap_install_from = egg - - import pkg_resources try: - pkg_resources.require("setuptools>="+version) - + import pkg_resources + except ImportError: + return do_download() + try: + pkg_resources.require("setuptools>="+version); return except pkg_resources.VersionConflict, e: - # XXX could we install in a subprocess here? - print >>sys.stderr, ( + if was_imported: + print >>sys.stderr, ( "The required version of setuptools (>=%s) is not available, and\n" "can't be installed while this script is running. Please install\n" - " a more recent version first.\n\n(Currently using %r)" - ) % (version, e.args[0]) - sys.exit(2) + " a more recent version first, using 'easy_install -U setuptools'." + "\n\n(Currently using %r)" + ) % (version, e.args[0]) + sys.exit(2) + else: + del pkg_resources, sys.modules['pkg_resources'] # reload ok + return do_download() + except pkg_resources.DistributionNotFound: + return do_download() def download_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, @@ -144,9 +153,43 @@ if dst: dst.close() return os.path.realpath(saveto) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" - try: import setuptools except ImportError: @@ -161,8 +204,11 @@ os.unlink(egg) else: if setuptools.__version__ == '0.0.1': - # tell the user to uninstall obsolete version - use_setuptools(version) + print >>sys.stderr, ( + "You have an obsolete version of setuptools installed. Please\n" + "remove it from your system entirely before rerunning this script." + ) + sys.exit(2) req = "setuptools>="+version import pkg_resources @@ -183,8 +229,6 @@ print "Setuptools version",version,"or greater has been installed." print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' - - def update_md5(filenames): """Update our built-in md5 registry""" @@ -221,3 +265,8 @@ update_md5(sys.argv[2:]) else: main(sys.argv[1:]) + + + + + Modified: doctools/trunk/sphinx/builder.py ============================================================================== --- doctools/trunk/sphinx/builder.py (original) +++ doctools/trunk/sphinx/builder.py Mon Aug 4 19:01:15 2008 @@ -145,6 +145,9 @@ node['uri'] = candidate else: candidate = node['uri'] + if candidate not in self.env.images: + # non-existing URI; let it alone + continue self.images[candidate] = self.env.images[candidate][1] # build methods Modified: doctools/trunk/sphinx/directives/code.py ============================================================================== --- doctools/trunk/sphinx/directives/code.py (original) +++ doctools/trunk/sphinx/directives/code.py Mon Aug 4 19:01:15 2008 @@ -8,6 +8,7 @@ """ import sys +import codecs from os import path from docutils import nodes @@ -67,13 +68,19 @@ lineno - state_machine.input_offset - 1))) fn = path.normpath(path.join(source_dir, rel_fn)) + encoding = options.get('encoding', 'utf-8') try: - f = open(fn) + f = codecs.open(fn, 'r', encoding) text = f.read() f.close() except (IOError, OSError): retnode = state.document.reporter.warning( 'Include file %r not found or reading it failed' % arguments[0], line=lineno) + except UnicodeError: + retnode = state.document.reporter.warning( + 'Encoding %r used for reading included file %r seems to ' + 'be wrong, try giving an :encoding: option' % + (encoding, arguments[0])) else: retnode = nodes.literal_block(text, text, source=fn) retnode.line = 1 @@ -85,7 +92,8 @@ return [retnode] literalinclude_directive.options = {'linenos': directives.flag, - 'language': directives.unchanged} + 'language': directives.unchanged, + 'encoding': directives.encoding} literalinclude_directive.content = 0 literalinclude_directive.arguments = (1, 0, 0) directives.register_directive('literalinclude', literalinclude_directive) Modified: doctools/trunk/sphinx/environment.py ============================================================================== --- doctools/trunk/sphinx/environment.py (original) +++ doctools/trunk/sphinx/environment.py Mon Aug 4 19:01:15 2008 @@ -559,12 +559,12 @@ imgpath = path.normpath(path.join(docdir, imguri)) node['uri'] = imgpath if imgpath.endswith(os.extsep + '*'): - for filename in glob(imgpath): - basename, ext = os.path.splitext(filename) - if ext == '.pdf': - candidates['application/pdf'] = filename - elif ext == '.svg': - candidates['image/svg+xml'] = filename + for filename in glob(path.join(self.srcdir, imgpath)): + dir, base = path.split(filename) + if base.lower().endswith('.pdf'): + candidates['application/pdf'] = path.join(docdir, base) + elif base.lower().endswith('.svg'): + candidates['image/svg+xml'] = path.join(docdir, base) else: f = open(filename, 'rb') try: @@ -572,23 +572,23 @@ finally: f.close() if imgtype: - candidates['image/' + imgtype] = filename + candidates['image/' + imgtype] = path.join(docdir, base) else: candidates['*'] = imgpath - for img in candidates.itervalues(): - self.dependencies.setdefault(docname, set()).add(img) - if not os.access(path.join(self.srcdir, img), os.R_OK): - self.warn(docname, 'Image file not readable: %s' % img, node.line) - if img in self.images: - self.images[img][0].add(docname) + for imgpath in candidates.itervalues(): + self.dependencies.setdefault(docname, set()).add(imgpath) + if not os.access(path.join(self.srcdir, imgpath), os.R_OK): + self.warn(docname, 'Image file not readable: %s' % imgpath, node.line) + if imgpath in self.images: + self.images[imgpath][0].add(docname) continue - uniquename = path.basename(img) + uniquename = path.basename(imgpath) base, ext = path.splitext(uniquename) i = 0 while uniquename in existing_names: i += 1 uniquename = '%s%s%s' % (base, i, ext) - self.images[img] = (set([docname]), uniquename) + self.images[imgpath] = (set([docname]), uniquename) existing_names.add(uniquename) def process_metadata(self, docname, doctree): Modified: doctools/trunk/sphinx/highlighting.py ============================================================================== --- doctools/trunk/sphinx/highlighting.py (original) +++ doctools/trunk/sphinx/highlighting.py Mon Aug 4 19:01:15 2008 @@ -128,6 +128,13 @@ if sys.version_info >= (2, 5): src = 'from __future__ import with_statement\n' + src + if isinstance(src, unicode): + # Non-ASCII chars will only occur in string literals + # and comments. If we wanted to give them to the parser + # correctly, we'd have to find out the correct source + # encoding. Since it may not even be given in a snippet, + # just replace all non-ASCII characters. + src = src.encode('ascii', 'replace') try: parser.suite(src) except parsing_exceptions: Modified: doctools/trunk/sphinx/quickstart.py ============================================================================== --- doctools/trunk/sphinx/quickstart.py (original) +++ doctools/trunk/sphinx/quickstart.py Mon Aug 4 19:01:15 2008 @@ -12,8 +12,10 @@ import sys, os, time from os import path +TERM_ENCODING = getattr(sys.stdin, 'encoding', None) + from sphinx.util import make_filename -from sphinx.util.console import purple, bold, red, nocolor +from sphinx.util.console import purple, bold, red, turquoise, nocolor PROMPT_PREFIX = '> ' @@ -56,8 +58,8 @@ master_doc = '%(master)s' # General substitutions. -project = %(project)r -copyright = '%(year)s, %(author)s' +project = u'%(project)s' +copyright = u'%(copyright)s' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. @@ -178,8 +180,8 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ - ('%(master)s', '%(project_fn)s.tex', '%(project)s Documentation', - '%(author)s', 'manual'), + ('%(master)s', '%(project_fn)s.tex', u'%(project_doc)s', + u'%(author)s', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -343,8 +345,18 @@ x = raw_input(prompt) if default and not x: x = default + if x.decode('ascii', 'replace').encode('ascii', 'replace') != x: + if TERM_ENCODING: + x = x.decode(TERM_ENCODING) + else: + print turquoise('* Note: non-ASCII characters entered and terminal ' + 'encoding unknown -- assuming UTF-8 or Latin-1.') + try: + x = x.decode('utf-8') + except UnicodeDecodeError: + x = x.decode('latin1') if validator and not validator(x): - print red(" * " + validator.__doc__) + print red('* ' + validator.__doc__) continue break d[key] = x @@ -414,12 +426,13 @@ os.name == 'posix' and 'y' or 'n', boolean) d['project_fn'] = make_filename(d['project']) - d['year'] = time.strftime('%Y') d['now'] = time.asctime() d['underline'] = len(d['project']) * '=' d['extensions'] = ', '.join( repr('sphinx.ext.' + name) for name in ('autodoc', 'doctest') if d['ext_' + name].upper() in ('Y', 'YES')) + d['copyright'] = time.strftime('%Y') + ', ' + d['author'] + d['project_doc'] = d['project'] + ' Documentation' if not path.isdir(d['path']): mkdir_p(d['path']) @@ -437,12 +450,12 @@ mkdir_p(path.join(srcdir, d['dot'] + 'static')) f = open(path.join(srcdir, 'conf.py'), 'w') - f.write(QUICKSTART_CONF % d) + f.write((QUICKSTART_CONF % d).encode('utf-8')) f.close() masterfile = path.join(srcdir, d['master'] + d['suffix']) f = open(masterfile, 'w') - f.write(MASTER_FILE % d) + f.write((MASTER_FILE % d).encode('utf-8')) f.close() create_makefile = d['makefile'].upper() in ('Y', 'YES') @@ -450,7 +463,7 @@ d['rsrcdir'] = separate and 'source' or '.' d['rbuilddir'] = separate and 'build' or d['dot'] + 'build' f = open(path.join(d['path'], 'Makefile'), 'w') - f.write(MAKEFILE % d) + f.write((MAKEFILE % d).encode('utf-8')) f.close() print Modified: doctools/trunk/sphinx/util/__init__.py ============================================================================== --- doctools/trunk/sphinx/util/__init__.py (original) +++ doctools/trunk/sphinx/util/__init__.py Mon Aug 4 19:01:15 2008 @@ -256,7 +256,7 @@ return filter(match, names) -no_fn_re = re.compile(r'[:/\\?*%|"\'<>. \t]') +no_fn_re = re.compile(r'[^a-zA-Z0-9_-]') def make_filename(string): return no_fn_re.sub('', string) Modified: doctools/trunk/tests/root/conf.py ============================================================================== --- doctools/trunk/tests/root/conf.py (original) +++ doctools/trunk/tests/root/conf.py Mon Aug 4 19:01:15 2008 @@ -32,7 +32,7 @@ source_suffix = '.txt' # The master toctree document. -master_doc = 'index' +master_doc = 'contents' # General substitutions. project = 'Sphinx Tests' Modified: doctools/trunk/tests/root/contents.txt ============================================================================== --- doctools/trunk/tests/root/contents.txt (original) +++ doctools/trunk/tests/root/contents.txt Mon Aug 4 19:01:15 2008 @@ -10,10 +10,12 @@ .. toctree:: :maxdepth: 2 + images + includes + Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` - Modified: doctools/trunk/tests/test_config.py ============================================================================== --- doctools/trunk/tests/test_config.py (original) +++ doctools/trunk/tests/test_config.py Mon Aug 4 19:01:15 2008 @@ -15,9 +15,9 @@ from sphinx.application import ExtensionError -def test_core_config(): - overrides = {'master_doc': 'master', 'nonexisting_value': 'True'} - cfg = TestApp(confoverrides=overrides).config + at with_testapp(confoverrides={'master_doc': 'master', 'nonexisting_value': 'True'}) +def test_core_config(app): + cfg = app.config # simple values assert 'project' in cfg.__dict__ @@ -61,8 +61,8 @@ assert cfg['project'] == cfg.project == 'Sphinx Tests' -def test_extension_values(): - app = TestApp() + at with_testapp() +def test_extension_values(app): cfg = app.config # default value Modified: doctools/trunk/tests/test_markup.py ============================================================================== --- doctools/trunk/tests/test_markup.py (original) +++ doctools/trunk/tests/test_markup.py Mon Aug 4 19:01:15 2008 @@ -9,6 +9,8 @@ :license: BSD. """ +import re + from util import * from docutils import frontend, utils, nodes @@ -18,11 +20,16 @@ from sphinx.htmlwriter import HTMLWriter, SmartyPantsHTMLTranslator from sphinx.latexwriter import LaTeXWriter, LaTeXTranslator -app = TestApp() -optparser = frontend.OptionParser(components=(rst.Parser, HTMLWriter, LaTeXWriter)) -settings = optparser.get_default_values() -settings.env = app.builder.env -parser = rst.Parser() +def setup_module(): + global app, settings, parser + app = TestApp() + optparser = frontend.OptionParser(components=(rst.Parser, HTMLWriter, LaTeXWriter)) + settings = optparser.get_default_values() + settings.env = app.builder.env + parser = rst.Parser() + +def teardown_module(): + app.cleanup() # since we're not resolving the markup afterwards, these nodes may remain class ForgivingTranslator: @@ -38,7 +45,7 @@ pass -def verify(rst, html_expected, latex_expected): +def verify_re(rst, html_expected, latex_expected): document = utils.new_document('test data', settings) parser.parse(rst, document) for msg in document.traverse(nodes.system_message): @@ -49,14 +56,17 @@ html_translator = ForgivingHTMLTranslator(app.builder, document) document.walkabout(html_translator) html_translated = ''.join(html_translator.fragment).strip() - assert html_translated == html_expected, 'from ' + rst + assert re.match(html_expected, html_translated), 'from' + rst if latex_expected: latex_translator = ForgivingLaTeXTranslator(document, app.builder) latex_translator.first_document = -1 # don't write \begin{document} document.walkabout(latex_translator) latex_translated = ''.join(latex_translator.body).strip() - assert latex_translated == latex_expected, 'from ' + rst + assert re.match(latex_expected, latex_translated), 'from ' + rst + +def verify(rst, html_expected, latex_expected): + verify_re(rst, re.escape(html_expected) + '$', re.escape(latex_expected) + '$') def test_inline(): @@ -78,9 +88,9 @@ '\\emph{a $\\rightarrow$ b}') # non-interpolation of dashes in option role - verify(':option:`--with-option`', - '

    --with-option

    ', - r'\emph{\texttt{--with-option}}') + verify_re(':option:`--with-option`', + '

    --with-option

    $', + r'\\emph{\\texttt{--with-option}}$') # verify smarty-pants quotes verify('"John"', '

    “John”

    ', "``John''") Modified: doctools/trunk/tests/test_quickstart.py ============================================================================== --- doctools/trunk/tests/test_quickstart.py (original) +++ doctools/trunk/tests/test_quickstart.py Mon Aug 4 19:01:15 2008 @@ -38,6 +38,7 @@ def teardown_module(): qs.raw_input = __builtin__.raw_input + qs.TERM_ENCODING = getattr(sys.stdin, 'encoding', None) coloron() @@ -108,10 +109,10 @@ 'Root path': tempdir, 'Separate source and build': 'y', 'Name prefix for templates': '_', - 'Project name': 'Sphinx Test', - 'Author name': 'Georg Brandl', - 'Project version': '0.1', - 'Project release': '0.1.1', + 'Project name': 'STASI\xe2\x84\xa2', + 'Author name': 'Wolfgang Sch\xc3\xa4uble', + 'Project version': '2.0', + 'Project release': '2.0.1', 'Source file suffix': '.txt', 'Name of your master document': 'contents', 'autodoc': 'y', @@ -119,6 +120,7 @@ 'Create Makefile': 'no', } qs.raw_input = mock_raw_input(answers, needanswer=True) + qs.TERM_ENCODING = 'utf-8' qs.inner_main([]) conffile = tempdir / 'source' / 'conf.py' @@ -129,14 +131,14 @@ assert ns['templates_path'] == ['_templates'] assert ns['source_suffix'] == '.txt' assert ns['master_doc'] == 'contents' - assert ns['project'] == 'Sphinx Test' - assert ns['copyright'] == '%s, Georg Brandl' % time.strftime('%Y') - assert ns['version'] == '0.1' - assert ns['release'] == '0.1.1' + assert ns['project'] == u'STASI?' + assert ns['copyright'] == u'%s, Wolfgang Sch?uble' % time.strftime('%Y') + assert ns['version'] == '2.0' + assert ns['release'] == '2.0.1' assert ns['html_static_path'] == ['_static'] assert ns['latex_documents'] == [ - ('contents', 'SphinxTest.tex', 'Sphinx Test Documentation', - 'Georg Brandl', 'manual')] + ('contents', 'STASI.tex', u'STASI? Documentation', + u'Wolfgang Sch?uble', 'manual')] assert (tempdir / 'build').isdir() assert (tempdir / 'source' / '_static').isdir() Modified: doctools/trunk/tests/util.py ============================================================================== --- doctools/trunk/tests/util.py (original) +++ doctools/trunk/tests/util.py Mon Aug 4 19:01:15 2008 @@ -8,19 +8,26 @@ """ import sys +import os import StringIO import tempfile +import shutil + +from functools import wraps from sphinx import application, builder from path import path +from nose import tools + __all__ = [ 'test_root', 'raises', 'raises_msg', - 'ErrorOutput', 'TestApp', + 'ListOutput', 'TestApp', 'with_testapp', 'path', 'with_tempdir', 'write_file', + 'sprint', ] @@ -59,15 +66,19 @@ (func.__name__, _excstr(exc))) -class ErrorOutput(object): +class ListOutput(object): """ - File-like object that raises :exc:`AssertionError` on ``write()``. + File-like object that collects written text in a list. """ def __init__(self, name): self.name = name + self.content = [] + + def reset(self): + del self.content[:] def write(self, text): - assert False, 'tried to write %r to %s' % (text, self.name) + self.content.append(text) class TestApp(application.Sphinx): @@ -84,20 +95,32 @@ if srcdir is None: srcdir = test_root + if srcdir == '(temp)': + tempdir = path(tempfile.mkdtemp()) / 'root' + test_root.copytree(tempdir) + srcdir = tempdir else: srcdir = path(srcdir) + self.builddir = srcdir.joinpath('_build') + if not self.builddir.isdir(): + self.builddir.makedirs() + self.made_builddir = True + else: + self.made_builddir = False if confdir is None: confdir = srcdir if outdir is None: - outdir = srcdir.joinpath('_build', buildername) + outdir = srcdir.joinpath(self.builddir, buildername) + if not outdir.isdir(): + outdir.makedirs() if doctreedir is None: - doctreedir = srcdir.joinpath(srcdir, '_build', 'doctrees') + doctreedir = srcdir.joinpath(srcdir, self.builddir, 'doctrees') if confoverrides is None: confoverrides = {} if status is None: status = StringIO.StringIO() if warning is None: - warning = ErrorOutput('stderr') + warning = ListOutput('stderr') if freshenv is None: freshenv = True @@ -105,6 +128,30 @@ buildername, confoverrides, status, warning, freshenv) + def cleanup(self): + trees = [self.outdir, self.doctreedir] + #f self.made_builddir: + # trees.append(self.builddir) + #for tree in trees: + # shutil.rmtree(tree, True) + + +def with_testapp(*args, **kwargs): + """ + Make a TestApp with args and kwargs, pass it to the test and clean up + properly. + """ + def generator(func): + @wraps(func) + def deco(*args2, **kwargs2): + app = TestApp(*args, **kwargs) + try: + func(app, *args2, **kwargs2) + finally: + app.cleanup() + return deco + return generator + def with_tempdir(func): def new_func(): @@ -119,3 +166,7 @@ f = open(str(name), 'wb') f.write(contents) f.close() + + +def sprint(*args): + sys.stderr.write(' '.join(map(str, args)) + '\n') From python-checkins at python.org Mon Aug 4 19:07:33 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 19:07:33 +0200 (CEST) Subject: [Python-checkins] r65498 - doctools/branches/0.4.x/sphinx/__init__.py Message-ID: <20080804170733.809581E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 19:07:33 2008 New Revision: 65498 Log: Absolutize doctreedir when parsing from commandline. Modified: doctools/branches/0.4.x/sphinx/__init__.py Modified: doctools/branches/0.4.x/sphinx/__init__.py ============================================================================== --- doctools/branches/0.4.x/sphinx/__init__.py (original) +++ doctools/branches/0.4.x/sphinx/__init__.py Mon Aug 4 19:07:33 2008 @@ -98,7 +98,7 @@ return 1 all_files = True elif opt == '-d': - doctreedir = val + doctreedir = path.abspath(val) elif opt == '-c': confdir = path.abspath(val) if not path.isfile(path.join(confdir, 'conf.py')): From python-checkins at python.org Mon Aug 4 19:17:49 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 19:17:49 +0200 (CEST) Subject: [Python-checkins] r65499 - in doctools/branches/0.4.x: CHANGES sphinx/application.py Message-ID: <20080804171749.E718B1E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 19:17:49 2008 New Revision: 65499 Log: If output and/or doctree directory are within the source directory, except them from the search for source files. Modified: doctools/branches/0.4.x/CHANGES doctools/branches/0.4.x/sphinx/application.py Modified: doctools/branches/0.4.x/CHANGES ============================================================================== --- doctools/branches/0.4.x/CHANGES (original) +++ doctools/branches/0.4.x/CHANGES Mon Aug 4 19:17:49 2008 @@ -1,6 +1,9 @@ Release 0.4.3 (in development) ============================== +* If output and/or doctree directory are within the source directory, + except them from the search for source files. + * Fix encoding handling for literal include files: ``literalinclude`` now has an ``encoding`` option that defaults to UTF-8. Modified: doctools/branches/0.4.x/sphinx/application.py ============================================================================== --- doctools/branches/0.4.x/sphinx/application.py (original) +++ doctools/branches/0.4.x/sphinx/application.py Mon Aug 4 19:17:49 2008 @@ -13,6 +13,7 @@ """ import sys +from os import path from docutils import nodes from docutils.parsers.rst import directives, roles @@ -64,10 +65,10 @@ self.builderclasses = builtin_builders.copy() self.builder = None - self.srcdir = srcdir - self.confdir = confdir - self.outdir = outdir - self.doctreedir = doctreedir + self.srcdir = path.abspath(srcdir) + self.confdir = path.abspath(confdir) + self.outdir = path.abspath(outdir) + self.doctreedir = path.abspath(doctreedir) self._status = status self._warning = warning @@ -88,6 +89,13 @@ # now that we know all config values, collect them from conf.py self.config.init_values() + # if the output and/or doctree dirs are within the source dir, except + # them from being searched for source files + if self.outdir.startswith(self.srcdir): + self.config.exclude_trees += [self.outdir[len(self.srcdir)+1:]] + if self.doctreedir.startswith(self.srcdir): + self.config.exclude_trees += [self.doctreedir[len(self.srcdir)+1:]] + if buildername is None: print >>status, 'No builder selected, using default: html' buildername = 'html' From python-checkins at python.org Mon Aug 4 19:31:26 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 19:31:26 +0200 (CEST) Subject: [Python-checkins] r65500 - in doctools/trunk: CHANGES doc/ext/intersphinx.rst doc/extensions.rst sphinx/builder.py sphinx/ext/intersphinx.py Message-ID: <20080804173126.C71BA1E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 19:31:25 2008 New Revision: 65500 Log: Add intersphinx extension. Added: doctools/trunk/doc/ext/intersphinx.rst (contents, props changed) doctools/trunk/sphinx/ext/intersphinx.py Modified: doctools/trunk/CHANGES doctools/trunk/doc/extensions.rst doctools/trunk/sphinx/builder.py Modified: doctools/trunk/CHANGES ============================================================================== --- doctools/trunk/CHANGES (original) +++ doctools/trunk/CHANGES Mon Aug 4 19:31:25 2008 @@ -4,6 +4,10 @@ New features added ------------------ +* The new extension ``sphinx.ext.intersphinx`` half-automatically + creates links to Sphinx documentation of Python objects in other + projects. + * Added a distutils command `build_sphinx`: When Sphinx is installed, you can call ``python setup.py build_sphinx`` for projects that have Sphinx documentation, which will build the docs and place them Added: doctools/trunk/doc/ext/intersphinx.rst ============================================================================== --- (empty file) +++ doctools/trunk/doc/ext/intersphinx.rst Mon Aug 4 19:31:25 2008 @@ -0,0 +1,67 @@ +:mod:`sphinx.ext.intersphinx` -- Link to other projects' documentation +====================================================================== + +.. module:: sphinx.ext.intersphinx + :synopsis: Link to other Sphinx documentation. + +.. index:: pair: automatic; linking + +.. versionadded:: 0.5 + +This extension can generate automatic links to the documentation of Python +objects in other projects. This works as follows: + +* Each Sphinx HTML build creates a file named :file:`objects.inv` that + contains a mapping from Python identifiers to URIs relative to the HTML set's + root. + +* Projects using the Intersphinx extension can specify the location of such + mapping files in the :confval:`intersphinx_mapping` config value. The mapping + will then be used to resolve otherwise missing references to Python objects + into links to the other documentation. + +* By default, the mapping file is assumed to be at the same location as the rest + of the documentation; however, the location of the mapping file can also be + specified individually, e.g. if the docs should be buildable without Internet + access. + +To use intersphinx linking, add ``'sphinx.ext.intersphinx'`` to your +:confval:`extensions` config value, and use these new config values to activate +linking: + +.. confval:: intersphinx_mapping + + A dictionary mapping URIs to either ``None`` or an URI. The keys are the + base URI of the foreign Sphinx documentation sets and can be local paths or + HTTP URIs. The values indicate where the inventory file can be found: they + can be ``None`` (at the same location as the base URI) or another local or + HTTP URI. + + Relative local paths in the keys are taken as relative to the base of the + built documentation, while relative local paths in the values are taken as + relative to the source directory. + + An example, to add links to modules and objects in the Python standard + library documentation:: + + intersphinx_mapping = {'http://docs.python.org/dev': None} + + This will download the corresponding :file:`objects.inv` file from the + Internet and generate links to the pages under the given URI. The downloaded + inventory is cached in the Sphinx environment, so it must be redownloaded + whenever you do a full rebuild. + + A second example, showing the meaning of a non-``None`` value:: + + intersphinx_mapping = {'http://docs.python.org/dev': 'python-inv.txt'} + + This will read the inventory from :file:`python.inv` in the source + directory, but still generate links to the pages under + ``http://docs.python.org/dev``. It is up to you to update the inventory file + as new objects are added to the Python documentation. + +.. confval:: intersphinx_cache_limit + + The maximum number of days to cache remote inventories. The default is + ``5``, meaning five days. Set this to a negative value to cache inventories + for unlimited time. Modified: doctools/trunk/doc/extensions.rst ============================================================================== --- doctools/trunk/doc/extensions.rst (original) +++ doctools/trunk/doc/extensions.rst Mon Aug 4 19:31:25 2008 @@ -34,6 +34,7 @@ ext/autodoc ext/doctest + ext/intersphinx ext/refcounting ext/ifconfig ext/coverage Modified: doctools/trunk/sphinx/builder.py ============================================================================== --- doctools/trunk/sphinx/builder.py (original) +++ doctools/trunk/sphinx/builder.py Mon Aug 4 19:31:25 2008 @@ -40,7 +40,7 @@ ENV_PICKLE_FILENAME = 'environment.pickle' LAST_BUILD_FILENAME = 'last_build' -INVENTORY_FILENAME = 'inventory.txt' +INVENTORY_FILENAME = 'objects.inv' class Builder(object): Added: doctools/trunk/sphinx/ext/intersphinx.py ============================================================================== --- (empty file) +++ doctools/trunk/sphinx/ext/intersphinx.py Mon Aug 4 19:31:25 2008 @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- +""" + sphinx.ext.intersphinx + ~~~~~~~~~~~~~~~~~~~~~~ + + Insert links to Python objects documented in remote Sphinx documentation. + + This works as follows: + + * Each Sphinx HTML build creates a file named "objects.inv" that contains + a mapping from Python identifiers to URIs relative to the HTML set's root. + + * Projects using the Intersphinx extension can specify links to such mapping + files in the `intersphinx_mapping` config value. The mapping will then be + used to resolve otherwise missing references to Python objects into links + to the other documentation. + + * By default, the mapping file is assumed to be at the same location as the + rest of the documentation; however, the location of the mapping file can + also be specified individually, e.g. if the docs should be buildable + without Internet access. + + :copyright: 2008 by Georg Brandl. + :license: BSD. +""" + +import time +import urllib +import posixpath +from os import path + +from docutils import nodes + +from sphinx.builder import INVENTORY_FILENAME + + +def fetch_inventory(app, uri, inv): + """Fetch, parse and return an intersphinx inventory file.""" + invdata = {} + # both *uri* (base URI of the links to generate) and *inv* (actual + # location of the inventory file) can be local or remote URIs + localuri = uri.find('://') == -1 + try: + if inv.find('://') != -1: + f = urllib.urlopen(inv) + else: + f = open(path.join(app.srcdir, inv)) + except Exception, err: + app.warn('intersphinx inventory %r not fetchable due to ' + '%s: %s' % (inv, err.__class__, err)) + return + try: + line = f.next() + if line.rstrip() != '# Sphinx inventory version 1': + raise ValueError('unknown or unsupported inventory version') + line = f.next() + projname = line.rstrip()[11:].decode('utf-8') + line = f.next() + version = line.rstrip()[11:] + for line in f: + name, type, location = line.rstrip().split(None, 2) + if localuri: + location = path.join(uri, location) + else: + location = posixpath.join(uri, location) + invdata[name] = (type, projname, version, location) + f.close() + except Exception, err: + app.warn('intersphinx inventory %r not readable due to ' + '%s: %s' % (inv, err.__class__, err)) + else: + return invdata + + +def load_mappings(app): + """Load all intersphinx mappings into the environment.""" + now = int(time.time()) + cache_time = now - app.config.intersphinx_cache_limit * 86400 + env = app.builder.env + if not hasattr(env, 'intersphinx_cache'): + env.intersphinx_cache = {} + cache = env.intersphinx_cache + update = False + for uri, inv in app.config.intersphinx_mapping.iteritems(): + # we can safely assume that the uri<->inv mapping is not changed + # during partial rebuilds since a changed intersphinx_mapping + # setting will cause a full environment reread + if not inv: + inv = posixpath.join(uri, INVENTORY_FILENAME) + # decide whether the inventory must be read: always read local + # files; remote ones only if the cache time is expired + if '://' not in inv or uri not in cache \ + or cache[uri][0] < cache_time: + invdata = fetch_inventory(app, uri, inv) + cache[uri] = (now, invdata) + update = True + if update: + env.intersphinx_inventory = {} + for _, invdata in cache.itervalues(): + if invdata: + env.intersphinx_inventory.update(invdata) + + +def missing_reference(app, env, node, contnode): + """Attempt to resolve a missing reference via intersphinx references.""" + type = node['reftype'] + target = node['reftarget'] + if type == 'mod': + type, proj, version, uri = env.intersphinx_inventory.get(target, + ('','','','')) + if type != 'mod': + return None + target = 'module-' + target # for link anchor + else: + if target[-2:] == '()': + target = target[:-2] + target = target.rstrip(' *') + # special case: exceptions and object methods + if type == 'exc' and '.' not in target and \ + 'exceptions.' + target in env.intersphinx_inventory: + target = 'exceptions.' + target + elif type in ('func', 'meth') and '.' not in target and \ + 'object.' + target in env.intersphinx_inventory: + target = 'object.' + target + if target not in env.intersphinx_inventory: + return None + type, proj, version, uri = env.intersphinx_inventory[target] + print "Intersphinx hit:", target, uri + newnode = nodes.reference('', '') + newnode['refuri'] = uri + '#' + target + newnode['reftitle'] = '(in %s v%s)' % (proj, version) + newnode['class'] = 'external-xref' + newnode.append(contnode) + return newnode + + +def setup(app): + app.add_config_value('intersphinx_mapping', {}, True) + app.add_config_value('intersphinx_cache_limit', 5, False) + app.connect('missing-reference', missing_reference) + app.connect('builder-inited', load_mappings) From python-checkins at python.org Mon Aug 4 19:38:14 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 19:38:14 +0200 (CEST) Subject: [Python-checkins] r65501 - doctools/trunk/sphinx/quickstart.py Message-ID: <20080804173814.571AB1E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 19:38:14 2008 New Revision: 65501 Log: Add intersphinx to quickstart. Modified: doctools/trunk/sphinx/quickstart.py Modified: doctools/trunk/sphinx/quickstart.py ============================================================================== --- doctools/trunk/sphinx/quickstart.py (original) +++ doctools/trunk/sphinx/quickstart.py Mon Aug 4 19:38:14 2008 @@ -202,6 +202,12 @@ #latex_use_modindex = True ''' +INTERSPHINX_CONFIG = ''' + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'http://docs.python.org/dev': None} +''' + MASTER_FILE = '''\ .. %(project)s documentation master file, created by sphinx-quickstart on %(now)s. You can adapt this file completely to your liking, but it should at least @@ -418,6 +424,8 @@ 'from modules (y/N)', 'n', boolean) do_prompt(d, 'ext_doctest', 'doctest: automatically test code snippets ' 'in doctest blocks (y/N)', 'n', boolean) + do_prompt(d, 'ext_intersphinx', 'intersphinx: link between Sphinx documentation ' + 'of different projects (y/N)', 'n', boolean) print ''' If you are under Unix, a Makefile can be generated for you so that you only have to run e.g. `make html' instead of invoking sphinx-build @@ -429,7 +437,7 @@ d['now'] = time.asctime() d['underline'] = len(d['project']) * '=' d['extensions'] = ', '.join( - repr('sphinx.ext.' + name) for name in ('autodoc', 'doctest') + repr('sphinx.ext.' + name) for name in ('autodoc', 'doctest', 'intersphinx') if d['ext_' + name].upper() in ('Y', 'YES')) d['copyright'] = time.strftime('%Y') + ', ' + d['author'] d['project_doc'] = d['project'] + ' Documentation' @@ -449,8 +457,12 @@ mkdir_p(path.join(srcdir, d['dot'] + 'templates')) mkdir_p(path.join(srcdir, d['dot'] + 'static')) + conf_text = QUICKSTART_CONF % d + if d['ext_intersphinx'].upper() in ('Y', 'YES'): + conf_text += INTERSPHINX_CONFIG + f = open(path.join(srcdir, 'conf.py'), 'w') - f.write((QUICKSTART_CONF % d).encode('utf-8')) + f.write(conf_text.encode('utf-8')) f.close() masterfile = path.join(srcdir, d['master'] + d['suffix']) From python-checkins at python.org Mon Aug 4 20:34:07 2008 From: python-checkins at python.org (gregory.p.smith) Date: Mon, 4 Aug 2008 20:34:07 +0200 (CEST) Subject: [Python-checkins] r65502 - python/trunk/Doc/library/subprocess.rst Message-ID: <20080804183407.725431E4004@bag.python.org> Author: gregory.p.smith Date: Mon Aug 4 20:34:07 2008 New Revision: 65502 Log: more cleanup ups of the recently added warnings in the subprocess docs. Modified: python/trunk/Doc/library/subprocess.rst Modified: python/trunk/Doc/library/subprocess.rst ============================================================================== --- python/trunk/Doc/library/subprocess.rst (original) +++ python/trunk/Doc/library/subprocess.rst Mon Aug 4 20:34:07 2008 @@ -196,8 +196,8 @@ .. warning:: This will deadlock if the child process generates enough output to a - stdout or stderr pipe causing it to block waiting for the OS's pipe buffer - to accept more data. + stdout or stderr pipe such that it blocks waiting for the OS pipe buffer + to accept more data. Use :meth:`communicate` to avoid that. .. method:: Popen.communicate(input=None) @@ -253,9 +253,10 @@ .. warning:: - Use :meth:`communicate` rather than ``.stdin.write()``, ``.stdout.read()`` or - ``.stderr.read`` to avoid deadlocks due to any of the other pipe buffers - filling up and blocking the child process. + Use :meth:`communicate` rather than :meth:`.stdin.write`, + :meth:`.stdout.read` or :meth:`.stderr.read` to avoid deadlocks due + to any of the other OS pipe buffers filling up and blocking the child + process. .. attribute:: Popen.stdin From python-checkins at python.org Mon Aug 4 21:39:06 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 21:39:06 +0200 (CEST) Subject: [Python-checkins] r65503 - in doctools/trunk: sphinx/ext/autodoc.py tests/root/conf.py tests/test_autodoc.py tests/test_quickstart.py tests/util.py Message-ID: <20080804193906.3851E1E4004@bag.python.org> Author: georg.brandl Date: Mon Aug 4 21:39:05 2008 New Revision: 65503 Log: Add a test suite for autodoc. Added: doctools/trunk/tests/test_autodoc.py (contents, props changed) Modified: doctools/trunk/sphinx/ext/autodoc.py doctools/trunk/tests/root/conf.py doctools/trunk/tests/test_quickstart.py doctools/trunk/tests/util.py Modified: doctools/trunk/sphinx/ext/autodoc.py ============================================================================== --- doctools/trunk/sphinx/ext/autodoc.py (original) +++ doctools/trunk/sphinx/ext/autodoc.py Mon Aug 4 21:39:05 2008 @@ -284,7 +284,7 @@ if what == 'module': if args or retann: self.warn('ignoring signature arguments and return annotation ' - 'for automodule %s' % mod) + 'for automodule %s' % fullname) return fullname, fullname, [], None, None elif what in ('class', 'exception', 'function'): @@ -335,18 +335,21 @@ else: # try to introspect the signature try: + args = None + getargs = True if what == 'class': # for classes, the relevant signature is the __init__ method's obj = getattr(obj, '__init__', None) # classes without __init__ method? if obj is None or obj is object.__init__ or not \ (inspect.ismethod(obj) or inspect.isfunction(obj)): - return '' - argspec = inspect.getargspec(obj) - if what in ('class', 'method') and argspec[0] and \ - argspec[0][0] in ('cls', 'self'): - del argspec[0][0] - args = inspect.formatargspec(*argspec) + getargs = False + if getargs: + argspec = inspect.getargspec(obj) + if what in ('class', 'method') and argspec[0] and \ + argspec[0][0] in ('cls', 'self'): + del argspec[0][0] + args = inspect.formatargspec(*argspec) except Exception, e: args = None err = e @@ -392,7 +395,7 @@ for part in objpath: todoc = getattr(todoc, part) except (ImportError, AttributeError), err: - self.warn('autodoc can\'t import/find %s %r, it reported error: "%s",' + self.warn('autodoc can\'t import/find %s %r, it reported error: "%s", ' 'please check your spelling and sys.path' % (what, str(fullname), err)) return Modified: doctools/trunk/tests/root/conf.py ============================================================================== --- doctools/trunk/tests/root/conf.py (original) +++ doctools/trunk/tests/root/conf.py Mon Aug 4 21:39:05 2008 @@ -23,7 +23,7 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['ext'] +extensions = ['ext', 'sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] Added: doctools/trunk/tests/test_autodoc.py ============================================================================== --- (empty file) +++ doctools/trunk/tests/test_autodoc.py Mon Aug 4 21:39:05 2008 @@ -0,0 +1,331 @@ +# -*- coding: utf-8 -*- +""" + test_autodoc + ~~~~~~~~~~~~ + + Test the autodoc extension. This tests mainly the RstGenerator; the auto + directives are tested in a test source file translated by test_build. + + :copyright: 2008 by Georg Brandl. + :license: BSD. +""" + +from util import * + +from docutils.statemachine import ViewList + +from sphinx.ext.autodoc import RstGenerator + + +def setup_module(): + global app, lid, options, gen + + app = TestApp() + app.builder.env.app = app + app.connect('autodoc-process-docstring', process_docstring) + app.connect('autodoc-process-signature', process_signature) + + options = Struct( + inherited_members = False, + undoc_members = False, + show_inheritance = False, + noindex = False, + synopsis = '', + platform = '', + deprecated = False, + ) + + gen = TestGenerator(options, app) + +def teardown_module(): + app.cleanup() + + +class TestGenerator(RstGenerator): + """Generator that handles warnings without a reporter.""" + + def __init__(self, options, app): + self.options = options + self.env = app.builder.env + self.lineno = 42 + self.filename_set = set() + self.warnings = [] + self.result = ViewList() + + def warn(self, msg): + self.warnings.append(msg) + + +processed_docstrings = [] + +def process_docstring(app, what, name, obj, options, lines): + processed_docstrings.append((what, name)) + if name == 'bar': + lines.extend(['42', '']) + +processed_signatures = [] + +def process_signature(app, what, name, obj, options, args, retann): + processed_signatures.append((what, name)) + if name == 'bar': + return '42', None + + +def test_resolve_name(): + # for modules + assert gen.resolve_name('module', 'test_autodoc') == \ + ('test_autodoc', 'test_autodoc', [], None, None) + assert gen.resolve_name('module', 'test.test_autodoc') == \ + ('test.test_autodoc', 'test.test_autodoc', [], None, None) + + assert gen.resolve_name('module', 'test(arg)') == \ + ('test', 'test', [], None, None) + assert 'ignoring signature arguments' in gen.warnings[0] + del gen.warnings[:] + + # for functions/classes + assert gen.resolve_name('function', 'util.raises') == \ + ('util.raises', 'util', ['raises'], None, None) + assert gen.resolve_name('function', 'util.raises(exc) -> None') == \ + ('util.raises', 'util', ['raises'], 'exc', ' -> None') + gen.env.autodoc_current_module = 'util' + assert gen.resolve_name('function', 'raises') == \ + ('raises', 'util', ['raises'], None, None) + gen.env.autodoc_current_module = None + gen.env.currmodule = 'util' + assert gen.resolve_name('function', 'raises') == \ + ('raises', 'util', ['raises'], None, None) + assert gen.resolve_name('class', 'TestApp') == \ + ('TestApp', 'util', ['TestApp'], None, None) + + # for members + gen.env.currmodule = 'foo' + assert gen.resolve_name('method', 'util.TestApp.cleanup') == \ + ('util.TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None) + gen.env.currmodule = 'util' + gen.env.currclass = 'Foo' + gen.env.autodoc_current_class = 'TestApp' + assert gen.resolve_name('method', 'cleanup') == \ + ('cleanup', 'util', ['TestApp', 'cleanup'], None, None) + assert gen.resolve_name('method', 'TestApp.cleanup') == \ + ('TestApp.cleanup', 'util', ['TestApp', 'cleanup'], None, None) + + # and clean up + gen.env.currmodule = None + gen.env.currclass = None + gen.env.autodoc_current_class = None + + +def test_format_signature(): + # no signatures for modules + assert gen.format_signature('module', 'test', None, None, None) == '' + + # test for functions + def f(a, b, c=1, **d): + pass + assert gen.format_signature('function', 'f', f, None, None) == '(a, b, c=1, **d)' + assert gen.format_signature('function', 'f', f, 'a, b, c, d', None) == \ + '(a, b, c, d)' + assert gen.format_signature('function', 'f', f, None, ' -> None') == \ + '(a, b, c=1, **d) -> None' + + # test for classes + class D: + pass + class E(object): + pass + # no signature for classes without __init__ + for C in (D, E): + assert gen.format_signature('class', 'D', C, None, None) == '' + class F: + def __init__(self, a, b=None): + pass + class G(F, object): + pass + for C in (F, G): + assert gen.format_signature('class', 'C', C, None, None) == '(a, b=None)' + assert gen.format_signature('class', 'C', D, 'a, b', ' -> X') == '(a, b) -> X' + + # test for methods + class H: + def foo1(self, b, *c): + pass + def foo2(b, *c): + pass + assert gen.format_signature('method', 'H.foo', H.foo1, None, None) == '(b, *c)' + assert gen.format_signature('method', 'H.foo', H.foo1, 'a', None) == '(a)' + assert gen.format_signature('method', 'H.foo', H.foo2, None, None) == '(b, *c)' + + # test exception handling + raises(RuntimeError, gen.format_signature, 'function', 'int', int, None, None) + + # test processing by event handler + assert gen.format_signature('method', 'bar', H.foo1, None, None) == '42' + + +def test_get_doc(): + def getdocl(*args): + # strip the empty line at the end + return list(gen.get_doc(*args))[:-1] + + # objects without docstring + def f(): + pass + assert getdocl('function', 'f', f) == [] + + # standard function, diverse docstring styles... + def f(): + """Docstring""" + def g(): + """ + Docstring + """ + for func in (f, g): + assert getdocl('function', 'f', func) == ['Docstring'] + + # first line vs. other lines indentation + def f(): + """First line + + Other + lines + """ + assert getdocl('function', 'f', f) == ['First line', '', 'Other', ' lines'] + + # charset guessing (this module is encoded in utf-8) + def f(): + """D?cstring""" + assert getdocl('function', 'f', f) == [u'D?cstring'] + + # already-unicode docstrings must be taken literally + def f(): + u"""D?cstring""" + assert getdocl('function', 'f', f) == [u'D?cstring'] + + # class docstring: depends on config value which one is taken + class C: + """Class docstring""" + def __init__(self): + """Init docstring""" + gen.env.config.autoclass_content = 'class' + assert getdocl('class', 'C', C) == ['Class docstring'] + gen.env.config.autoclass_content = 'init' + assert getdocl('class', 'C', C) == ['Init docstring'] + gen.env.config.autoclass_content = 'both' + assert getdocl('class', 'C', C) == ['Class docstring', '', 'Init docstring'] + + class D: + def __init__(self): + """Init docstring""" + + # docstring processing by event handler + assert getdocl('class', 'bar', D) == ['Init docstring', '', '42'] + + +def test_generate(): + def assert_warns(warn_str, *args): + gen.generate(*args) + assert len(gen.result) == 0, gen.result + assert len(gen.warnings) == 1, gen.warnings + assert warn_str in gen.warnings[0], gen.warnings + del gen.warnings[:] + + def assert_works(*args): + gen.generate(*args) + assert gen.result + assert len(gen.warnings) == 0, gen.warnings + del gen.result[:] + + def assert_processes(items, *args): + del processed_docstrings[:] + del processed_signatures[:] + assert_works(*args) + assert set(processed_docstrings) | set(processed_signatures) == set(items) + + def assert_result_contains(item, *args): + gen.generate(*args) + assert len(gen.warnings) == 0, gen.warnings + assert item in gen.result + del gen.result[:] + + # no module found? + assert_warns("import for autodocumenting 'foobar'", + 'function', 'foobar', None, None) + # importing + assert_warns("import/find module 'test_foobar'", + 'module', 'test_foobar', None, None) + # attributes missing + assert_warns("import/find function 'util.foobar'", + 'function', 'util.foobar', None, None) + + # test auto and given content mixing + gen.env.currmodule = 'test_autodoc' + assert_result_contains(' Function.', 'method', 'Class.meth', [], None) + add_content = ViewList() + add_content.append('Content.', '', 0) + assert_result_contains(' Function.', 'method', 'Class.meth', [], add_content) + assert_result_contains(' Content.', 'method', 'Class.meth', [], add_content) + + # test check_module + gen.generate('function', 'raises', None, None, check_module=True) + assert len(gen.result) == 0 + + # assert that exceptions can be documented + assert_works('exception', 'test_autodoc.CustomEx', ['__all__'], None) + assert_works('exception', 'test_autodoc.CustomEx', [], None) + + # test diverse inclusion settings for members + should = [('class', 'Class')] + assert_processes(should, 'class', 'Class', [], None) + should.extend([('method', 'Class.meth')]) + assert_processes(should, 'class', 'Class', ['meth'], None) + should.extend([('attribute', 'Class.prop')]) + assert_processes(should, 'class', 'Class', ['__all__'], None) + options.undoc_members = True + should.append(('method', 'Class.undocmeth')) + assert_processes(should, 'class', 'Class', ['__all__'], None) + options.inherited_members = True + should.append(('method', 'Class.inheritedmeth')) + assert_processes(should, 'class', 'Class', ['__all__'], None) + + # test module flags + assert_result_contains('.. module:: test_autodoc', 'module', + 'test_autodoc', [], None) + options.synopsis = 'Synopsis' + assert_result_contains(' :synopsis: Synopsis', 'module', 'test_autodoc', [], None) + options.deprecated = True + assert_result_contains(' :deprecated:', 'module', 'test_autodoc', [], None) + options.platform = 'Platform' + assert_result_contains(' :platform: Platform', 'module', 'test_autodoc', [], None) + + # test noindex flag + options.noindex = True + assert_result_contains(' :noindex:', 'module', 'test_autodoc', [], None) + assert_result_contains(' :noindex:', 'class', 'Base', [], None) + + +# --- generate fodder ------------ + +class CustomEx(Exception): + """My custom exception.""" + + def f(self): + """Exception method.""" + + +class Base(object): + def inheritedmeth(self): + """Inherited function.""" + +class Class(Base): + """Class to document.""" + + def meth(self): + """Function.""" + + def undocmeth(self): + pass + + @property + def prop(self): + """Property.""" Modified: doctools/trunk/tests/test_quickstart.py ============================================================================== --- doctools/trunk/tests/test_quickstart.py (original) +++ doctools/trunk/tests/test_quickstart.py Mon Aug 4 21:39:05 2008 @@ -117,6 +117,7 @@ 'Name of your master document': 'contents', 'autodoc': 'y', 'doctest': 'yes', + 'intersphinx': 'no', 'Create Makefile': 'no', } qs.raw_input = mock_raw_input(answers, needanswer=True) Modified: doctools/trunk/tests/util.py ============================================================================== --- doctools/trunk/tests/util.py (original) +++ doctools/trunk/tests/util.py Mon Aug 4 21:39:05 2008 @@ -24,7 +24,7 @@ __all__ = [ 'test_root', - 'raises', 'raises_msg', + 'raises', 'raises_msg', 'Struct', 'ListOutput', 'TestApp', 'with_testapp', 'path', 'with_tempdir', 'write_file', 'sprint', @@ -66,6 +66,11 @@ (func.__name__, _excstr(exc))) +class Struct(object): + def __init__(self, **kwds): + self.__dict__.update(kwds) + + class ListOutput(object): """ File-like object that collects written text in a list. From python-checkins at python.org Mon Aug 4 22:16:18 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 22:16:18 +0200 (CEST) Subject: [Python-checkins] r65504 - in doctools/trunk: sphinx/application.py sphinx/ext/autodoc.py tests/test_autodoc.py Message-ID: <20080804201618.BAA021E4006@bag.python.org> Author: georg.brandl Date: Mon Aug 4 22:16:18 2008 New Revision: 65504 Log: Add tests for between() and cut_lines() and fix them. Also fix a bug in the application interface. Modified: doctools/trunk/sphinx/application.py doctools/trunk/sphinx/ext/autodoc.py doctools/trunk/tests/test_autodoc.py Modified: doctools/trunk/sphinx/application.py ============================================================================== --- doctools/trunk/sphinx/application.py (original) +++ doctools/trunk/sphinx/application.py Mon Aug 4 22:16:18 2008 @@ -159,7 +159,7 @@ return listener_id def disconnect(self, listener_id): - for event in self._listeners: + for event in self._listeners.itervalues(): event.pop(listener_id, None) def emit(self, event, *args): Modified: doctools/trunk/sphinx/ext/autodoc.py ============================================================================== --- doctools/trunk/sphinx/ext/autodoc.py (original) +++ doctools/trunk/sphinx/ext/autodoc.py Mon Aug 4 22:16:18 2008 @@ -111,7 +111,13 @@ return del lines[:pre] if post: + # remove one trailing blank line. + if lines and not lines[-1]: + lines.pop(-1) del lines[-post:] + # make sure there is a blank line at the end + if lines and lines[-1]: + lines.append('') return process def between(marker, what=None, keepempty=False): @@ -141,6 +147,9 @@ deleted += 1 if not lines and not keepempty: lines[:] = orig_lines + # make sure there is a blank line at the end + if lines and lines[-1]: + lines.append('') return process @@ -340,10 +349,14 @@ if what == 'class': # for classes, the relevant signature is the __init__ method's obj = getattr(obj, '__init__', None) - # classes without __init__ method? + # classes without __init__ method, default __init__ or + # __init__ written in C? if obj is None or obj is object.__init__ or not \ (inspect.ismethod(obj) or inspect.isfunction(obj)): getargs = False + elif inspect.isbuiltin(obj) or inspect.ismethoddescriptor(obj): + # can never get arguments of a C function or method + getargs = False if getargs: argspec = inspect.getargspec(obj) if what in ('class', 'method') and argspec[0] and \ Modified: doctools/trunk/tests/test_autodoc.py ============================================================================== --- doctools/trunk/tests/test_autodoc.py (original) +++ doctools/trunk/tests/test_autodoc.py Mon Aug 4 22:16:18 2008 @@ -14,7 +14,7 @@ from docutils.statemachine import ViewList -from sphinx.ext.autodoc import RstGenerator +from sphinx.ext.autodoc import RstGenerator, cut_lines, between def setup_module(): @@ -222,6 +222,30 @@ assert getdocl('class', 'bar', D) == ['Init docstring', '', '42'] +def test_docstring_processing_functions(): + lid = app.connect('autodoc-process-docstring', cut_lines(1, 1, ['function'])) + def f(): + """ + first line + second line + third line + """ + assert list(gen.get_doc('function', 'f', f)) == ['second line', ''] + app.disconnect(lid) + + lid = app.connect('autodoc-process-docstring', between('---', ['function'])) + def f(): + """ + first line + --- + second line + --- + third line + """ + assert list(gen.get_doc('function', 'f', f)) == ['second line', ''] + app.disconnect(lid) + + def test_generate(): def assert_warns(warn_str, *args): gen.generate(*args) @@ -246,6 +270,7 @@ gen.generate(*args) assert len(gen.warnings) == 0, gen.warnings assert item in gen.result + print '\n'.join(gen.result) del gen.result[:] # no module found? @@ -303,6 +328,10 @@ assert_result_contains(' :noindex:', 'module', 'test_autodoc', [], None) assert_result_contains(' :noindex:', 'class', 'Base', [], None) + # okay, now let's get serious about mixing Python and C signature stuff + assert_result_contains('.. class:: CustomDict', 'class', 'CustomDict', + ['__all__'], None) + # --- generate fodder ------------ @@ -329,3 +358,6 @@ @property def prop(self): """Property.""" + +class CustomDict(dict): + """Docstring.""" From python-checkins at python.org Mon Aug 4 22:16:35 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 22:16:35 +0200 (CEST) Subject: [Python-checkins] r65505 - in doctools/branches/0.4.x/sphinx: application.py ext/autodoc.py Message-ID: <20080804201635.AB7041E4012@bag.python.org> Author: georg.brandl Date: Mon Aug 4 22:16:35 2008 New Revision: 65505 Log: Backport fixes found in trunk test. Modified: doctools/branches/0.4.x/sphinx/application.py doctools/branches/0.4.x/sphinx/ext/autodoc.py Modified: doctools/branches/0.4.x/sphinx/application.py ============================================================================== --- doctools/branches/0.4.x/sphinx/application.py (original) +++ doctools/branches/0.4.x/sphinx/application.py Mon Aug 4 22:16:35 2008 @@ -165,7 +165,7 @@ return listener_id def disconnect(self, listener_id): - for event in self._listeners: + for event in self._listeners.itervalues(): event.pop(listener_id, None) def emit(self, event, *args): Modified: doctools/branches/0.4.x/sphinx/ext/autodoc.py ============================================================================== --- doctools/branches/0.4.x/sphinx/ext/autodoc.py (original) +++ doctools/branches/0.4.x/sphinx/ext/autodoc.py Mon Aug 4 22:16:35 2008 @@ -252,6 +252,8 @@ # classes without __init__ method? if obj is None or obj is object.__init__: return '' + if inspect.isbuiltin(obj) or inspect.ismethoddescriptor(obj): + return '' argspec = inspect.getargspec(obj) if what in ('class', 'method') and argspec[0] and \ argspec[0][0] in ('cls', 'self'): From python-checkins at python.org Mon Aug 4 23:08:00 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:08:00 +0200 (CEST) Subject: [Python-checkins] r65506 - python/trunk/Lib/re.py Message-ID: <20080804210800.21F391E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:07:59 2008 New Revision: 65506 Log: Remove the use of callable() in re to silence warnings under -3. Modified: python/trunk/Lib/re.py Modified: python/trunk/Lib/re.py ============================================================================== --- python/trunk/Lib/re.py (original) +++ python/trunk/Lib/re.py Mon Aug 4 23:07:59 2008 @@ -316,7 +316,7 @@ if i == j: break action = self.lexicon[m.lastindex-1][1] - if callable(action): + if hasattr(action, '__call__'): self.match = m action = action(self, m.group()) if action is not None: From python-checkins at python.org Mon Aug 4 23:10:50 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:10:50 +0200 (CEST) Subject: [Python-checkins] r65508 - in python/trunk/Lib/xml/sax: __init__.py xmlreader.py Message-ID: <20080804211050.A94811E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:10:50 2008 New Revision: 65508 Log: Remove dict.has_key() usage in xml.sax to silence warnings under -3. Modified: python/trunk/Lib/xml/sax/__init__.py python/trunk/Lib/xml/sax/xmlreader.py Modified: python/trunk/Lib/xml/sax/__init__.py ============================================================================== --- python/trunk/Lib/xml/sax/__init__.py (original) +++ python/trunk/Lib/xml/sax/__init__.py Mon Aug 4 23:10:50 2008 @@ -81,7 +81,7 @@ return _create_parser(parser_name) except ImportError,e: import sys - if sys.modules.has_key(parser_name): + if parser_name in sys.modules: # The parser module was found, but importing it # failed unexpectedly, pass this exception through raise Modified: python/trunk/Lib/xml/sax/xmlreader.py ============================================================================== --- python/trunk/Lib/xml/sax/xmlreader.py (original) +++ python/trunk/Lib/xml/sax/xmlreader.py Mon Aug 4 23:10:50 2008 @@ -294,12 +294,12 @@ return self._attrs[name] def getNameByQName(self, name): - if not self._attrs.has_key(name): + if not name in self._attrs: raise KeyError, name return name def getQNameByName(self, name): - if not self._attrs.has_key(name): + if not name in self._attrs: raise KeyError, name return name @@ -319,7 +319,7 @@ return self._attrs.keys() def has_key(self, name): - return self._attrs.has_key(name) + return name in self._attrs def __contains__(self, name): return self._attrs.has_key(name) From python-checkins at python.org Mon Aug 4 23:17:15 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:17:15 +0200 (CEST) Subject: [Python-checkins] r65510 - python/trunk/Lib/shelve.py Message-ID: <20080804211715.4D0141E4007@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:17:15 2008 New Revision: 65510 Log: Remove dict.has_key() usage in the shelve module to silence warnings under -3. Modified: python/trunk/Lib/shelve.py Modified: python/trunk/Lib/shelve.py ============================================================================== --- python/trunk/Lib/shelve.py (original) +++ python/trunk/Lib/shelve.py Mon Aug 4 23:17:15 2008 @@ -105,13 +105,13 @@ return len(self.dict) def has_key(self, key): - return self.dict.has_key(key) + return key in self.dict def __contains__(self, key): - return self.dict.has_key(key) + return key in self.dict def get(self, key, default=None): - if self.dict.has_key(key): + if key in self.dict: return self[key] return default From python-checkins at python.org Mon Aug 4 23:19:41 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:19:41 +0200 (CEST) Subject: [Python-checkins] r65512 - python/trunk/Lib/sqlite3/dbapi2.py Message-ID: <20080804211941.B00361E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:19:41 2008 New Revision: 65512 Log: Remove usage of apply() in sqlite3 to silence warnings under -3. Modified: python/trunk/Lib/sqlite3/dbapi2.py Modified: python/trunk/Lib/sqlite3/dbapi2.py ============================================================================== --- python/trunk/Lib/sqlite3/dbapi2.py (original) +++ python/trunk/Lib/sqlite3/dbapi2.py Mon Aug 4 23:19:41 2008 @@ -39,13 +39,13 @@ Timestamp = datetime.datetime def DateFromTicks(ticks): - return apply(Date, time.localtime(ticks)[:3]) + return Date(*time.localtime(ticks)[:3]) def TimeFromTicks(ticks): - return apply(Time, time.localtime(ticks)[3:6]) + return Time(*time.localtime(ticks)[3:6]) def TimestampFromTicks(ticks): - return apply(Timestamp, time.localtime(ticks)[:6]) + return Timestamp(*time.localtime(ticks)[:6]) version_info = tuple([int(x) for x in version.split(".")]) sqlite_version_info = tuple([int(x) for x in sqlite_version.split(".")]) From python-checkins at python.org Mon Aug 4 23:23:07 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:23:07 +0200 (CEST) Subject: [Python-checkins] r65514 - python/trunk/Lib/tarfile.py Message-ID: <20080804212307.E1EA71E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:23:07 2008 New Revision: 65514 Log: Remove a dict.has_key() and list.sort(cmp=) usage from tarfile to silence warnings under -3. Modified: python/trunk/Lib/tarfile.py Modified: python/trunk/Lib/tarfile.py ============================================================================== --- python/trunk/Lib/tarfile.py (original) +++ python/trunk/Lib/tarfile.py Mon Aug 4 23:23:07 2008 @@ -51,6 +51,7 @@ import struct import copy import re +import operator if sys.platform == 'mac': # This module needs work for MacOS9, especially in the area of pathname @@ -1401,7 +1402,7 @@ next._apply_pax_info(pax_headers, tarfile.encoding, tarfile.errors) next.offset = self.offset - if pax_headers.has_key("size"): + if "size" in pax_headers: # If the extended header replaces the size field, # we need to recalculate the offset where the next # header starts. @@ -2027,7 +2028,7 @@ self.extract(tarinfo, path) # Reverse sort directories. - directories.sort(lambda a, b: cmp(a.name, b.name)) + directories.sort(key=operator.attrgetter('name')) directories.reverse() # Set correct owner, mtime and filemode on directories. From python-checkins at python.org Mon Aug 4 23:24:44 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:24:44 +0200 (CEST) Subject: [Python-checkins] r65516 - python/trunk/Lib/lib-tk/Tkinter.py Message-ID: <20080804212444.32ED41E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:24:43 2008 New Revision: 65516 Log: Remove a use of callable() from Tkinter to silence warnings under -3. Modified: python/trunk/Lib/lib-tk/Tkinter.py Modified: python/trunk/Lib/lib-tk/Tkinter.py ============================================================================== --- python/trunk/Lib/lib-tk/Tkinter.py (original) +++ python/trunk/Lib/lib-tk/Tkinter.py Mon Aug 4 23:24:43 2008 @@ -1577,7 +1577,7 @@ """Bind function FUNC to command NAME for this widget. Return the function bound to NAME if None is given. NAME could be e.g. "WM_SAVE_YOURSELF" or "WM_DELETE_WINDOW".""" - if callable(func): + if hasattr(func, '__call__'): command = self._register(func) else: command = func From python-checkins at python.org Mon Aug 4 23:30:10 2008 From: python-checkins at python.org (mark.dickinson) Date: Mon, 4 Aug 2008 23:30:10 +0200 (CEST) Subject: [Python-checkins] r65518 - in python/trunk: Lib/test/test_long.py Misc/NEWS Objects/longobject.c Message-ID: <20080804213010.1DDC01E4006@bag.python.org> Author: mark.dickinson Date: Mon Aug 4 23:30:09 2008 New Revision: 65518 Log: Issue #1481296: (again!) Make conversion of a float NaN to an int or long raise ValueError instead of returning 0. Also, change the error message for conversion of an infinity to an integer, replacing 'long' by 'integer', so that it's appropriate for both long(float('inf')) and int(float('inf')). Modified: python/trunk/Lib/test/test_long.py python/trunk/Misc/NEWS python/trunk/Objects/longobject.c Modified: python/trunk/Lib/test/test_long.py ============================================================================== --- python/trunk/Lib/test/test_long.py (original) +++ python/trunk/Lib/test/test_long.py Mon Aug 4 23:30:09 2008 @@ -745,7 +745,8 @@ def test_nan_inf(self): self.assertRaises(OverflowError, long, float('inf')) - self.assertEqual(long(float('nan')), 0L) + self.assertRaises(OverflowError, long, float('-inf')) + self.assertRaises(ValueError, long, float('nan')) def test_main(): test_support.run_unittest(LongTest) Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Mon Aug 4 23:30:09 2008 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #1481296: Make long(float('nan')) and int(float('nan')) raise + ValueError consistently across platforms. + - Issue #3479: On platforms where sizeof(int) is smaller than sizeof(long) (64bit Unix, for example), unichr() would truncate its argument and return u'\x00' for unichr(2**32). Now it properly raises an OverflowError. Modified: python/trunk/Objects/longobject.c ============================================================================== --- python/trunk/Objects/longobject.c (original) +++ python/trunk/Objects/longobject.c Mon Aug 4 23:30:09 2008 @@ -176,11 +176,13 @@ neg = 0; if (Py_IS_INFINITY(dval)) { PyErr_SetString(PyExc_OverflowError, - "cannot convert float infinity to long"); + "cannot convert float infinity to integer"); return NULL; } if (Py_IS_NAN(dval)) { - return PyLong_FromLong(0L); + PyErr_SetString(PyExc_ValueError, + "cannot convert float NaN to integer"); + return NULL; } if (dval < 0.0) { neg = 1; From python-checkins at python.org Mon Aug 4 23:30:53 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:30:53 +0200 (CEST) Subject: [Python-checkins] r65519 - in python/trunk/Lib/wsgiref: handlers.py headers.py simple_server.py util.py Message-ID: <20080804213053.9C7861E400A@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:30:53 2008 New Revision: 65519 Log: Silence warnings under -3 triggered by wsgiref. Modified: python/trunk/Lib/wsgiref/handlers.py python/trunk/Lib/wsgiref/headers.py python/trunk/Lib/wsgiref/simple_server.py python/trunk/Lib/wsgiref/util.py Modified: python/trunk/Lib/wsgiref/handlers.py ============================================================================== --- python/trunk/Lib/wsgiref/handlers.py (original) +++ python/trunk/Lib/wsgiref/handlers.py Mon Aug 4 23:30:53 2008 @@ -17,12 +17,13 @@ d[k] = v return d -try: - True - False -except NameError: - True = not None - False = not True +# Uncomment for 2.2 compatibility. +#try: +# True +# False +#except NameError: +# True = not None +# False = not True # Weekday and month names for HTTP date/time formatting; always English! Modified: python/trunk/Lib/wsgiref/headers.py ============================================================================== --- python/trunk/Lib/wsgiref/headers.py (original) +++ python/trunk/Lib/wsgiref/headers.py Mon Aug 4 23:30:53 2008 @@ -63,7 +63,7 @@ Does *not* raise an exception if the header is missing. """ name = name.lower() - self._headers[:] = [kv for kv in self._headers if kv[0].lower()<>name] + self._headers[:] = [kv for kv in self._headers if kv[0].lower() != name] def __getitem__(self,name): """Get the first header value for 'name' @@ -142,7 +142,7 @@ return self._headers[:] def __repr__(self): - return "Headers(%s)" % `self._headers` + return "Headers(%r)" % self._headers def __str__(self): """str() returns the formatted headers, complete with end line, Modified: python/trunk/Lib/wsgiref/simple_server.py ============================================================================== --- python/trunk/Lib/wsgiref/simple_server.py (original) +++ python/trunk/Lib/wsgiref/simple_server.py Mon Aug 4 23:30:53 2008 @@ -169,7 +169,7 @@ print >>stdout h = environ.items(); h.sort() for k,v in h: - print >>stdout, k,'=',`v` + print >>stdout, k,'=', repr(v) start_response("200 OK", [('Content-Type','text/plain')]) return [stdout.getvalue()] Modified: python/trunk/Lib/wsgiref/util.py ============================================================================== --- python/trunk/Lib/wsgiref/util.py (original) +++ python/trunk/Lib/wsgiref/util.py Mon Aug 4 23:30:53 2008 @@ -98,7 +98,7 @@ return None path_parts = path_info.split('/') - path_parts[1:-1] = [p for p in path_parts[1:-1] if p and p<>'.'] + path_parts[1:-1] = [p for p in path_parts[1:-1] if p and p != '.'] name = path_parts[1] del path_parts[1] @@ -166,7 +166,7 @@ 'connection':1, 'keep-alive':1, 'proxy-authenticate':1, 'proxy-authorization':1, 'te':1, 'trailers':1, 'transfer-encoding':1, 'upgrade':1 -}.has_key +}.__contains__ def is_hop_by_hop(header_name): """Return true if 'header_name' is an HTTP/1.1 "Hop-by-Hop" header""" From python-checkins at python.org Mon Aug 4 23:32:55 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 23:32:55 +0200 (CEST) Subject: [Python-checkins] r65521 - doctools/trunk Message-ID: <20080804213255.5E1DE1E4006@bag.python.org> Author: georg.brandl Date: Mon Aug 4 23:32:55 2008 New Revision: 65521 Log: Blocked revisions 65505 via svnmerge ........ r65505 | georg.brandl | 2008-08-04 20:16:35 +0000 (Mon, 04 Aug 2008) | 2 lines Backport fixes found in trunk test. ........ Modified: doctools/trunk/ (props changed) From python-checkins at python.org Mon Aug 4 23:33:00 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:33:00 +0200 (CEST) Subject: [Python-checkins] r65522 - python/trunk/Lib/aifc.py Message-ID: <20080804213300.746A11E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:33:00 2008 New Revision: 65522 Log: Remove tuple parameter unpacking in aifc to silence warnings under -3. Modified: python/trunk/Lib/aifc.py Modified: python/trunk/Lib/aifc.py ============================================================================== --- python/trunk/Lib/aifc.py (original) +++ python/trunk/Lib/aifc.py Mon Aug 4 23:33:00 2008 @@ -665,7 +665,8 @@ ## raise Error, 'cannot change parameters after starting to write' ## self._version = version - def setparams(self, (nchannels, sampwidth, framerate, nframes, comptype, compname)): + def setparams(self, info): + nchannels, sampwidth, framerate, nframes, comptype, compname = info if self._nframeswritten: raise Error, 'cannot change parameters after starting to write' if comptype not in ('NONE', 'ULAW', 'ALAW', 'G722'): From python-checkins at python.org Mon Aug 4 23:34:34 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:34:34 +0200 (CEST) Subject: [Python-checkins] r65524 - python/trunk/Lib/pickle.py Message-ID: <20080804213434.536161E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:34:34 2008 New Revision: 65524 Log: Remove use of callable() from pickle to silence warnings under -3. Modified: python/trunk/Lib/pickle.py Modified: python/trunk/Lib/pickle.py ============================================================================== --- python/trunk/Lib/pickle.py (original) +++ python/trunk/Lib/pickle.py Mon Aug 4 23:34:34 2008 @@ -351,7 +351,7 @@ raise PicklingError("args from reduce() should be a tuple") # Assert that func is callable - if not callable(func): + if not hasattr(func, '__call__'): raise PicklingError("func from reduce should be callable") save = self.save From buildbot at python.org Mon Aug 4 23:40:22 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 21:40:22 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo trunk Message-ID: <20080804214033.DD9D31E4006@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/1270 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon,gregory.p.smith,nick.coghlan BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 23:42:47 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 21:42:47 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable 3.0 Message-ID: <20080804214247.8B0971E4007@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%203.0/builds/1306 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_xmlrpc_net make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Mon Aug 4 23:46:30 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 21:46:30 +0000 Subject: [Python-checkins] buildbot failure in ia64 Ubuntu 3.0 Message-ID: <20080804214630.3CAA31E4006@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%20Ubuntu%203.0/builds/366 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ia64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/home/pybot/buildarea/3.0.klose-debian-ia64/build/Lib/io.py", line 1462, in write self.flush() File "/home/pybot/buildarea/3.0.klose-debian-ia64/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/home/pybot/buildarea/3.0.klose-debian-ia64/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Mon Aug 4 23:46:41 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 23:46:41 +0200 (CEST) Subject: [Python-checkins] r65526 - in doctools/branches/0.4.x: sphinx/builder.py sphinx/textwriter.py tests/root/conf.py tests/test_build.py tests/util.py Message-ID: <20080804214641.8110B1E4006@bag.python.org> Author: georg.brandl Date: Mon Aug 4 23:46:41 2008 New Revision: 65526 Log: Let the test suite run the text, linkcheck, and changes builders. Modified: doctools/branches/0.4.x/sphinx/builder.py doctools/branches/0.4.x/sphinx/textwriter.py doctools/branches/0.4.x/tests/root/conf.py doctools/branches/0.4.x/tests/test_build.py doctools/branches/0.4.x/tests/util.py Modified: doctools/branches/0.4.x/sphinx/builder.py ============================================================================== --- doctools/branches/0.4.x/sphinx/builder.py (original) +++ doctools/branches/0.4.x/sphinx/builder.py Mon Aug 4 23:46:41 2008 @@ -976,6 +976,9 @@ libchanges = {} apichanges = [] otherchanges = {} + if version not in self.env.versionchanges: + self.info(bold('no changes in this version.')) + return self.info(bold('writing summary file...')) for type, docname, lineno, module, descname, content in \ self.env.versionchanges[version]: Modified: doctools/branches/0.4.x/sphinx/textwriter.py ============================================================================== --- doctools/branches/0.4.x/sphinx/textwriter.py (original) +++ doctools/branches/0.4.x/sphinx/textwriter.py Mon Aug 4 23:46:41 2008 @@ -350,6 +350,7 @@ def visit_image(self, node): self.add_text('[image]') + raise nodes.SkipNode def visit_transition(self, node): indent = sum(self.stateindent) Modified: doctools/branches/0.4.x/tests/root/conf.py ============================================================================== --- doctools/branches/0.4.x/tests/root/conf.py (original) +++ doctools/branches/0.4.x/tests/root/conf.py Mon Aug 4 23:46:41 2008 @@ -58,6 +58,7 @@ # List of directories, relative to source directories, that shouldn't be searched # for source files. #exclude_dirs = [] +exclude_trees = ['_build'] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True Modified: doctools/branches/0.4.x/tests/test_build.py ============================================================================== --- doctools/branches/0.4.x/tests/test_build.py (original) +++ doctools/branches/0.4.x/tests/test_build.py Mon Aug 4 23:46:41 2008 @@ -10,11 +10,12 @@ """ import os +import difflib import htmlentitydefs from StringIO import StringIO -from etree13 import ElementTree as ET from util import * +from etree13 import ElementTree as ET from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder @@ -66,14 +67,17 @@ def test_html(app): app.builder.build_all() html_warnings = html_warnfile.getvalue().replace(os.sep, '/') - assert html_warnings == HTML_WARNINGS % {'root': app.srcdir} + html_warnings_exp = HTML_WARNINGS % {'root': app.srcdir} + assert html_warnings == html_warnings_exp, 'Warnings don\'t match:\n' + \ + '\n'.join(difflib.ndiff(html_warnings_exp.splitlines(), + html_warnings.splitlines())) if not ET: return for fname, paths in HTML_XPATH.iteritems(): parser = NslessParser() parser.entity.update(htmlentitydefs.entitydefs) - etree = ET.parse(app.outdir / fname, parser) + etree = ET.parse(os.path.join(app.outdir, fname), parser) for path, text in paths.iteritems(): nodes = list(etree.findall(path)) assert nodes != [] @@ -92,4 +96,22 @@ def test_latex(app): app.builder.build_all() latex_warnings = latex_warnfile.getvalue().replace(os.sep, '/') - assert latex_warnings == LATEX_WARNINGS % {'root': app.srcdir} + latex_warnings_exp = LATEX_WARNINGS % {'root': app.srcdir} + assert latex_warnings == latex_warnings_exp, 'Warnings don\'t match:\n' + \ + '\n'.join(difflib.ndiff(latex_warnings_exp.splitlines(), + latex_warnings.splitlines())) + + +# just let the remaining ones run for now + + at with_testapp(buildername='linkcheck') +def test_linkcheck(app): + app.builder.build_all() + + at with_testapp(buildername='text') +def test_text(app): + app.builder.build_all() + + at with_testapp(buildername='changes') +def test_changes(app): + app.builder.build_all() Modified: doctools/branches/0.4.x/tests/util.py ============================================================================== --- doctools/branches/0.4.x/tests/util.py (original) +++ doctools/branches/0.4.x/tests/util.py Mon Aug 4 23:46:41 2008 @@ -130,10 +130,10 @@ def cleanup(self): trees = [self.outdir, self.doctreedir] - #f self.made_builddir: - # trees.append(self.builddir) - #for tree in trees: - # shutil.rmtree(tree, True) + if self.made_builddir: + trees.append(self.builddir) + for tree in trees: + shutil.rmtree(tree, True) def with_testapp(*args, **kwargs): From python-checkins at python.org Mon Aug 4 23:48:12 2008 From: python-checkins at python.org (georg.brandl) Date: Mon, 4 Aug 2008 23:48:12 +0200 (CEST) Subject: [Python-checkins] r65527 - in doctools/trunk: sphinx/__init__.py sphinx/application.py sphinx/builder.py sphinx/textwriter.py tests/root/conf.py tests/test_build.py tests/util.py Message-ID: <20080804214812.C737D1E4006@bag.python.org> Author: georg.brandl Date: Mon Aug 4 23:48:12 2008 New Revision: 65527 Log: Merged revisions 65498-65499,65526 via svnmerge from svn+ssh://pythondev at svn.python.org/doctools/branches/0.4.x ........ r65498 | georg.brandl | 2008-08-04 17:07:33 +0000 (Mon, 04 Aug 2008) | 2 lines Absolutize doctreedir when parsing from commandline. ........ r65499 | georg.brandl | 2008-08-04 17:17:49 +0000 (Mon, 04 Aug 2008) | 4 lines If output and/or doctree directory are within the source directory, except them from the search for source files. ........ r65526 | georg.brandl | 2008-08-04 21:46:41 +0000 (Mon, 04 Aug 2008) | 2 lines Let the test suite run the text, linkcheck, and changes builders. ........ Modified: doctools/trunk/ (props changed) doctools/trunk/sphinx/__init__.py doctools/trunk/sphinx/application.py doctools/trunk/sphinx/builder.py doctools/trunk/sphinx/textwriter.py doctools/trunk/tests/root/conf.py doctools/trunk/tests/test_build.py doctools/trunk/tests/util.py Modified: doctools/trunk/sphinx/__init__.py ============================================================================== --- doctools/trunk/sphinx/__init__.py (original) +++ doctools/trunk/sphinx/__init__.py Mon Aug 4 23:48:12 2008 @@ -98,7 +98,7 @@ return 1 all_files = True elif opt == '-d': - doctreedir = val + doctreedir = path.abspath(val) elif opt == '-c': confdir = path.abspath(val) if not path.isfile(path.join(confdir, 'conf.py')): Modified: doctools/trunk/sphinx/application.py ============================================================================== --- doctools/trunk/sphinx/application.py (original) +++ doctools/trunk/sphinx/application.py Mon Aug 4 23:48:12 2008 @@ -13,6 +13,7 @@ """ import sys +from os import path from docutils import nodes from docutils.parsers.rst import directives, roles @@ -66,10 +67,10 @@ self.builderclasses = builtin_builders.copy() self.builder = None - self.srcdir = srcdir - self.confdir = confdir - self.outdir = outdir - self.doctreedir = doctreedir + self.srcdir = path.abspath(srcdir) + self.confdir = path.abspath(confdir) + self.outdir = path.abspath(outdir) + self.doctreedir = path.abspath(doctreedir) self._status = status self._warning = warning @@ -90,6 +91,13 @@ # now that we know all config values, collect them from conf.py self.config.init_values() + # if the output and/or doctree dirs are within the source dir, except + # them from being searched for source files + if self.outdir.startswith(self.srcdir): + self.config.exclude_trees += [self.outdir[len(self.srcdir)+1:]] + if self.doctreedir.startswith(self.srcdir): + self.config.exclude_trees += [self.doctreedir[len(self.srcdir)+1:]] + if buildername is None: print >>status, 'No builder selected, using default: html' buildername = 'html' Modified: doctools/trunk/sphinx/builder.py ============================================================================== --- doctools/trunk/sphinx/builder.py (original) +++ doctools/trunk/sphinx/builder.py Mon Aug 4 23:48:12 2008 @@ -1030,6 +1030,9 @@ libchanges = {} apichanges = [] otherchanges = {} + if version not in self.env.versionchanges: + self.info(bold('no changes in this version.')) + return self.info(bold('writing summary file...')) for type, docname, lineno, module, descname, content in \ self.env.versionchanges[version]: Modified: doctools/trunk/sphinx/textwriter.py ============================================================================== --- doctools/trunk/sphinx/textwriter.py (original) +++ doctools/trunk/sphinx/textwriter.py Mon Aug 4 23:48:12 2008 @@ -350,6 +350,7 @@ def visit_image(self, node): self.add_text('[image]') + raise nodes.SkipNode def visit_transition(self, node): indent = sum(self.stateindent) Modified: doctools/trunk/tests/root/conf.py ============================================================================== --- doctools/trunk/tests/root/conf.py (original) +++ doctools/trunk/tests/root/conf.py Mon Aug 4 23:48:12 2008 @@ -58,6 +58,7 @@ # List of directories, relative to source directories, that shouldn't be searched # for source files. #exclude_dirs = [] +exclude_trees = ['_build'] # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True Modified: doctools/trunk/tests/test_build.py ============================================================================== --- doctools/trunk/tests/test_build.py (original) +++ doctools/trunk/tests/test_build.py Mon Aug 4 23:48:12 2008 @@ -10,11 +10,12 @@ """ import os +import difflib import htmlentitydefs from StringIO import StringIO -from etree13 import ElementTree as ET from util import * +from etree13 import ElementTree as ET from sphinx.builder import StandaloneHTMLBuilder, LaTeXBuilder @@ -66,14 +67,17 @@ def test_html(app): app.builder.build_all() html_warnings = html_warnfile.getvalue().replace(os.sep, '/') - assert html_warnings == HTML_WARNINGS % {'root': app.srcdir} + html_warnings_exp = HTML_WARNINGS % {'root': app.srcdir} + assert html_warnings == html_warnings_exp, 'Warnings don\'t match:\n' + \ + '\n'.join(difflib.ndiff(html_warnings_exp.splitlines(), + html_warnings.splitlines())) if not ET: return for fname, paths in HTML_XPATH.iteritems(): parser = NslessParser() parser.entity.update(htmlentitydefs.entitydefs) - etree = ET.parse(app.outdir / fname, parser) + etree = ET.parse(os.path.join(app.outdir, fname), parser) for path, text in paths.iteritems(): nodes = list(etree.findall(path)) assert nodes != [] @@ -92,4 +96,22 @@ def test_latex(app): app.builder.build_all() latex_warnings = latex_warnfile.getvalue().replace(os.sep, '/') - assert latex_warnings == LATEX_WARNINGS % {'root': app.srcdir} + latex_warnings_exp = LATEX_WARNINGS % {'root': app.srcdir} + assert latex_warnings == latex_warnings_exp, 'Warnings don\'t match:\n' + \ + '\n'.join(difflib.ndiff(latex_warnings_exp.splitlines(), + latex_warnings.splitlines())) + + +# just let the remaining ones run for now + + at with_testapp(buildername='linkcheck') +def test_linkcheck(app): + app.builder.build_all() + + at with_testapp(buildername='text') +def test_text(app): + app.builder.build_all() + + at with_testapp(buildername='changes') +def test_changes(app): + app.builder.build_all() Modified: doctools/trunk/tests/util.py ============================================================================== --- doctools/trunk/tests/util.py (original) +++ doctools/trunk/tests/util.py Mon Aug 4 23:48:12 2008 @@ -135,10 +135,10 @@ def cleanup(self): trees = [self.outdir, self.doctreedir] - #f self.made_builddir: - # trees.append(self.builddir) - #for tree in trees: - # shutil.rmtree(tree, True) + if self.made_builddir: + trees.append(self.builddir) + for tree in trees: + shutil.rmtree(tree, True) def with_testapp(*args, **kwargs): From python-checkins at python.org Mon Aug 4 23:52:26 2008 From: python-checkins at python.org (brett.cannon) Date: Mon, 4 Aug 2008 23:52:26 +0200 (CEST) Subject: [Python-checkins] r65528 - python/trunk/Misc/NEWS Message-ID: <20080804215226.3D2141E4006@bag.python.org> Author: brett.cannon Date: Mon Aug 4 23:52:25 2008 New Revision: 65528 Log: Add a note about all the modules/packages changed to silence -3 warnings. More changes are needed once some decisions are made, but this is the work up to this point. Modified: python/trunk/Misc/NEWS Modified: python/trunk/Misc/NEWS ============================================================================== --- python/trunk/Misc/NEWS (original) +++ python/trunk/Misc/NEWS Mon Aug 4 23:52:25 2008 @@ -41,6 +41,13 @@ Library ------- +- Changed code in the following modules/packages to remove warnings raised + while running under the ``-3`` flag: aifc, asyncore, bdb, bsddb, + ConfigParser, cookielib, DocXMLRPCServer, email, filecmp, fileinput, inspect, + logging, modulefinder, pdb, pickle, profile, pstats, pydoc, re, rlcompleter, + SimpleXMLRPCServer, shelve, sqlite3, tarfile, Tkinter, test.test_support, + textwrap, threading, tokenize, traceback, urlparse, wsgiref, xml, xmlrpclib. + - Issue #3039: Fix tarfile.TarFileCompat.writestr() which always raised an AttributeError. From buildbot at python.org Tue Aug 5 00:12:19 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 22:12:19 +0000 Subject: [Python-checkins] buildbot failure in OS X x86 3.0 Message-ID: <20080804221219.6CAE81E4006@bag.python.org> The Buildbot has detected a new failure of OS X x86 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/OS%20X%20x86%203.0/builds/132 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: noller-osx86 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/threading.py", line 492, in _bootstrap_inner self.run() File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/threading.py", line 447, in run self._target(*self._args, **self._kwargs) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/bsddb/test/test_thread.py", line 89, in writerThread self._writerThread(*args, **kwargs) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/bsddb/test/test_thread.py", line 278, in _writerThread self.assertEqual(data, self.makeData(key)) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/unittest.py", line 325, in failUnlessEqual raise self.failureException(msg or '%r != %r' % (first, second)) AssertionError: None != b'1001-1001-1001-1001-1001' Traceback (most recent call last): File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/threading.py", line 492, in _bootstrap_inner self.run() File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/threading.py", line 447, in run self._target(*self._args, **self._kwargs) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/bsddb/test/test_thread.py", line 89, in writerThread self._writerThread(*args, **kwargs) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/bsddb/test/test_thread.py", line 278, in _writerThread self.assertEqual(data, self.makeData(key)) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/unittest.py", line 325, in failUnlessEqual raise self.failureException(msg or '%r != %r' % (first, second)) AssertionError: None != b'2000-2000-2000-2000-2000' Traceback (most recent call last): File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/threading.py", line 492, in _bootstrap_inner self.run() File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/threading.py", line 447, in run self._target(*self._args, **self._kwargs) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/bsddb/test/test_thread.py", line 89, in writerThread self._writerThread(*args, **kwargs) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/bsddb/test/test_thread.py", line 278, in _writerThread self.assertEqual(data, self.makeData(key)) File "/Users/buildbot/buildarea/3.0.noller-osx86/build/Lib/unittest.py", line 325, in failUnlessEqual raise self.failureException(msg or '%r != %r' % (first, second)) AssertionError: None != b'0002-0002-0002-0002-0002' 1 test failed: test_tempfile make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 00:12:42 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 22:12:42 +0000 Subject: [Python-checkins] buildbot failure in x86 osx.5 3.0 Message-ID: <20080804221242.567131E4006@bag.python.org> The Buildbot has detected a new failure of x86 osx.5 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20osx.5%203.0/builds/119 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-x86-osx5 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: 1 test failed: test_mailbox make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 00:19:31 2008 From: python-checkins at python.org (georg.brandl) Date: Tue, 5 Aug 2008 00:19:31 +0200 (CEST) Subject: [Python-checkins] r65529 - in doctools/branches/0.4.x: CHANGES sphinx/application.py sphinx/environment.py sphinx/quickstart.py tests/root tests/root/_build tests/root/conf.py tests/test_build.py tests/test_markup.py tests/util.py Message-ID: <20080804221931.02B311E4006@bag.python.org> Author: georg.brandl Date: Tue Aug 5 00:19:30 2008 New Revision: 65529 Log: Revert r65499 which was not well thought out. Instead, put the whole build dir in exclude_trees by default in quickstart. Also, revisit application cleanup and make it consistently use less setup time while still trying to leave to traces. Added: doctools/branches/0.4.x/tests/root/_build/ (props changed) Modified: doctools/branches/0.4.x/CHANGES doctools/branches/0.4.x/sphinx/application.py doctools/branches/0.4.x/sphinx/environment.py doctools/branches/0.4.x/sphinx/quickstart.py doctools/branches/0.4.x/tests/root/ (props changed) doctools/branches/0.4.x/tests/root/conf.py doctools/branches/0.4.x/tests/test_build.py doctools/branches/0.4.x/tests/test_markup.py doctools/branches/0.4.x/tests/util.py Modified: doctools/branches/0.4.x/CHANGES ============================================================================== --- doctools/branches/0.4.x/CHANGES (original) +++ doctools/branches/0.4.x/CHANGES Tue Aug 5 00:19:30 2008 @@ -1,9 +1,6 @@ Release 0.4.3 (in development) ============================== -* If output and/or doctree directory are within the source directory, - except them from the search for source files. - * Fix encoding handling for literal include files: ``literalinclude`` now has an ``encoding`` option that defaults to UTF-8. Modified: doctools/branches/0.4.x/sphinx/application.py ============================================================================== --- doctools/branches/0.4.x/sphinx/application.py (original) +++ doctools/branches/0.4.x/sphinx/application.py Tue Aug 5 00:19:30 2008 @@ -13,7 +13,6 @@ """ import sys -from os import path from docutils import nodes from docutils.parsers.rst import directives, roles @@ -65,10 +64,10 @@ self.builderclasses = builtin_builders.copy() self.builder = None - self.srcdir = path.abspath(srcdir) - self.confdir = path.abspath(confdir) - self.outdir = path.abspath(outdir) - self.doctreedir = path.abspath(doctreedir) + self.srcdir = srcdir + self.confdir = confdir + self.outdir = outdir + self.doctreedir = doctreedir self._status = status self._warning = warning @@ -89,13 +88,6 @@ # now that we know all config values, collect them from conf.py self.config.init_values() - # if the output and/or doctree dirs are within the source dir, except - # them from being searched for source files - if self.outdir.startswith(self.srcdir): - self.config.exclude_trees += [self.outdir[len(self.srcdir)+1:]] - if self.doctreedir.startswith(self.srcdir): - self.config.exclude_trees += [self.doctreedir[len(self.srcdir)+1:]] - if buildername is None: print >>status, 'No builder selected, using default: html' buildername = 'html' Modified: doctools/branches/0.4.x/sphinx/environment.py ============================================================================== --- doctools/branches/0.4.x/sphinx/environment.py (original) +++ doctools/branches/0.4.x/sphinx/environment.py Tue Aug 5 00:19:30 2008 @@ -575,7 +575,8 @@ for imgpath in candidates.itervalues(): self.dependencies.setdefault(docname, set()).add(imgpath) if not os.access(path.join(self.srcdir, imgpath), os.R_OK): - self.warn(docname, 'Image file not readable: %s' % imgpath, node.line) + self.warn(docname, 'Image file not readable: %s' % imgpath, + node.line) if imgpath in self.images: self.images[imgpath][0].add(docname) continue Modified: doctools/branches/0.4.x/sphinx/quickstart.py ============================================================================== --- doctools/branches/0.4.x/sphinx/quickstart.py (original) +++ doctools/branches/0.4.x/sphinx/quickstart.py Tue Aug 5 00:19:30 2008 @@ -80,7 +80,7 @@ # List of directories, relative to source directories, that shouldn't be searched # for source files. -#exclude_dirs = [] +exclude_trees = [%(exclude_trees)s] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None @@ -438,8 +438,10 @@ mkdir_p(srcdir) if separate: builddir = path.join(d['path'], 'build') + d['exclude_trees'] = '' else: builddir = path.join(srcdir, d['dot'] + 'build') + d['exclude_trees'] = repr(d['dot'] + 'build') mkdir_p(builddir) mkdir_p(path.join(srcdir, d['dot'] + 'templates')) mkdir_p(path.join(srcdir, d['dot'] + 'static')) Modified: doctools/branches/0.4.x/tests/root/conf.py ============================================================================== --- doctools/branches/0.4.x/tests/root/conf.py (original) +++ doctools/branches/0.4.x/tests/root/conf.py Tue Aug 5 00:19:30 2008 @@ -57,7 +57,6 @@ # List of directories, relative to source directories, that shouldn't be searched # for source files. -#exclude_dirs = [] exclude_trees = ['_build'] # If true, '()' will be appended to :func: etc. cross-reference text. Modified: doctools/branches/0.4.x/tests/test_build.py ============================================================================== --- doctools/branches/0.4.x/tests/test_build.py (original) +++ doctools/branches/0.4.x/tests/test_build.py Tue Aug 5 00:19:30 2008 @@ -112,6 +112,6 @@ def test_text(app): app.builder.build_all() - at with_testapp(buildername='changes') + at with_testapp(buildername='changes', cleanenv=True) def test_changes(app): app.builder.build_all() Modified: doctools/branches/0.4.x/tests/test_markup.py ============================================================================== --- doctools/branches/0.4.x/tests/test_markup.py (original) +++ doctools/branches/0.4.x/tests/test_markup.py Tue Aug 5 00:19:30 2008 @@ -22,7 +22,7 @@ def setup_module(): global app, settings, parser - app = TestApp() + app = TestApp(cleanenv=True) optparser = frontend.OptionParser(components=(rst.Parser, HTMLWriter, LaTeXWriter)) settings = optparser.get_default_values() settings.env = app.builder.env Modified: doctools/branches/0.4.x/tests/util.py ============================================================================== --- doctools/branches/0.4.x/tests/util.py (original) +++ doctools/branches/0.4.x/tests/util.py Tue Aug 5 00:19:30 2008 @@ -89,32 +89,34 @@ def __init__(self, srcdir=None, confdir=None, outdir=None, doctreedir=None, buildername='html', confoverrides=None, status=None, warning=None, - freshenv=None, confname='conf.py'): + freshenv=None, confname='conf.py', cleanenv=False): application.CONFIG_FILENAME = confname + self.cleanup_trees = [] + if srcdir is None: srcdir = test_root if srcdir == '(temp)': - tempdir = path(tempfile.mkdtemp()) / 'root' - test_root.copytree(tempdir) - srcdir = tempdir + tempdir = path(tempfile.mkdtemp()) + self.cleanup_trees.append(tempdir) + temproot = tempdir / 'root' + test_root.copytree(temproot) + srcdir = temproot else: srcdir = path(srcdir) self.builddir = srcdir.joinpath('_build') - if not self.builddir.isdir(): - self.builddir.makedirs() - self.made_builddir = True - else: - self.made_builddir = False if confdir is None: confdir = srcdir if outdir is None: outdir = srcdir.joinpath(self.builddir, buildername) if not outdir.isdir(): outdir.makedirs() + self.cleanup_trees.insert(0, outdir) if doctreedir is None: doctreedir = srcdir.joinpath(srcdir, self.builddir, 'doctrees') + if cleanenv: + self.cleanup_trees.insert(0, doctreedir) if confoverrides is None: confoverrides = {} if status is None: @@ -122,17 +124,14 @@ if warning is None: warning = ListOutput('stderr') if freshenv is None: - freshenv = True + freshenv = False application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir, buildername, confoverrides, status, warning, freshenv) - def cleanup(self): - trees = [self.outdir, self.doctreedir] - if self.made_builddir: - trees.append(self.builddir) - for tree in trees: + def cleanup(self, doctrees=False): + for tree in self.cleanup_trees: shutil.rmtree(tree, True) From python-checkins at python.org Tue Aug 5 00:20:45 2008 From: python-checkins at python.org (georg.brandl) Date: Tue, 5 Aug 2008 00:20:45 +0200 (CEST) Subject: [Python-checkins] r65530 - in doctools/trunk: sphinx/application.py sphinx/environment.py sphinx/quickstart.py tests/root tests/root/_build tests/root/conf.py tests/test_build.py tests/test_markup.py tests/util.py Message-ID: <20080804222045.88E591E400D@bag.python.org> Author: georg.brandl Date: Tue Aug 5 00:20:44 2008 New Revision: 65530 Log: Merged revisions 65529 via svnmerge from svn+ssh://pythondev at svn.python.org/doctools/branches/0.4.x ........ r65529 | georg.brandl | 2008-08-04 22:19:30 +0000 (Mon, 04 Aug 2008) | 6 lines Revert r65499 which was not well thought out. Instead, put the whole build dir in exclude_trees by default in quickstart. Also, revisit application cleanup and make it consistently use less setup time while still trying to leave to traces. ........ Added: doctools/trunk/tests/root/_build/ (props changed) - copied from r65529, /doctools/branches/0.4.x/tests/root/_build/ Modified: doctools/trunk/ (props changed) doctools/trunk/sphinx/application.py doctools/trunk/sphinx/environment.py doctools/trunk/sphinx/quickstart.py doctools/trunk/tests/root/ (props changed) doctools/trunk/tests/root/conf.py doctools/trunk/tests/test_build.py doctools/trunk/tests/test_markup.py doctools/trunk/tests/util.py Modified: doctools/trunk/sphinx/application.py ============================================================================== --- doctools/trunk/sphinx/application.py (original) +++ doctools/trunk/sphinx/application.py Tue Aug 5 00:20:44 2008 @@ -13,7 +13,6 @@ """ import sys -from os import path from docutils import nodes from docutils.parsers.rst import directives, roles @@ -67,10 +66,10 @@ self.builderclasses = builtin_builders.copy() self.builder = None - self.srcdir = path.abspath(srcdir) - self.confdir = path.abspath(confdir) - self.outdir = path.abspath(outdir) - self.doctreedir = path.abspath(doctreedir) + self.srcdir = srcdir + self.confdir = confdir + self.outdir = outdir + self.doctreedir = doctreedir self._status = status self._warning = warning @@ -91,13 +90,6 @@ # now that we know all config values, collect them from conf.py self.config.init_values() - # if the output and/or doctree dirs are within the source dir, except - # them from being searched for source files - if self.outdir.startswith(self.srcdir): - self.config.exclude_trees += [self.outdir[len(self.srcdir)+1:]] - if self.doctreedir.startswith(self.srcdir): - self.config.exclude_trees += [self.doctreedir[len(self.srcdir)+1:]] - if buildername is None: print >>status, 'No builder selected, using default: html' buildername = 'html' Modified: doctools/trunk/sphinx/environment.py ============================================================================== --- doctools/trunk/sphinx/environment.py (original) +++ doctools/trunk/sphinx/environment.py Tue Aug 5 00:20:44 2008 @@ -578,7 +578,8 @@ for imgpath in candidates.itervalues(): self.dependencies.setdefault(docname, set()).add(imgpath) if not os.access(path.join(self.srcdir, imgpath), os.R_OK): - self.warn(docname, 'Image file not readable: %s' % imgpath, node.line) + self.warn(docname, 'Image file not readable: %s' % imgpath, + node.line) if imgpath in self.images: self.images[imgpath][0].add(docname) continue Modified: doctools/trunk/sphinx/quickstart.py ============================================================================== --- doctools/trunk/sphinx/quickstart.py (original) +++ doctools/trunk/sphinx/quickstart.py Tue Aug 5 00:20:44 2008 @@ -80,7 +80,7 @@ # List of directories, relative to source directories, that shouldn't be searched # for source files. -#exclude_dirs = [] +exclude_trees = [%(exclude_trees)s] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None @@ -451,8 +451,10 @@ mkdir_p(srcdir) if separate: builddir = path.join(d['path'], 'build') + d['exclude_trees'] = '' else: builddir = path.join(srcdir, d['dot'] + 'build') + d['exclude_trees'] = repr(d['dot'] + 'build') mkdir_p(builddir) mkdir_p(path.join(srcdir, d['dot'] + 'templates')) mkdir_p(path.join(srcdir, d['dot'] + 'static')) Modified: doctools/trunk/tests/root/conf.py ============================================================================== --- doctools/trunk/tests/root/conf.py (original) +++ doctools/trunk/tests/root/conf.py Tue Aug 5 00:20:44 2008 @@ -57,7 +57,6 @@ # List of directories, relative to source directories, that shouldn't be searched # for source files. -#exclude_dirs = [] exclude_trees = ['_build'] # If true, '()' will be appended to :func: etc. cross-reference text. Modified: doctools/trunk/tests/test_build.py ============================================================================== --- doctools/trunk/tests/test_build.py (original) +++ doctools/trunk/tests/test_build.py Tue Aug 5 00:20:44 2008 @@ -112,6 +112,6 @@ def test_text(app): app.builder.build_all() - at with_testapp(buildername='changes') + at with_testapp(buildername='changes', cleanenv=True) def test_changes(app): app.builder.build_all() Modified: doctools/trunk/tests/test_markup.py ============================================================================== --- doctools/trunk/tests/test_markup.py (original) +++ doctools/trunk/tests/test_markup.py Tue Aug 5 00:20:44 2008 @@ -22,7 +22,7 @@ def setup_module(): global app, settings, parser - app = TestApp() + app = TestApp(cleanenv=True) optparser = frontend.OptionParser(components=(rst.Parser, HTMLWriter, LaTeXWriter)) settings = optparser.get_default_values() settings.env = app.builder.env Modified: doctools/trunk/tests/util.py ============================================================================== --- doctools/trunk/tests/util.py (original) +++ doctools/trunk/tests/util.py Tue Aug 5 00:20:44 2008 @@ -94,32 +94,34 @@ def __init__(self, srcdir=None, confdir=None, outdir=None, doctreedir=None, buildername='html', confoverrides=None, status=None, warning=None, - freshenv=None, confname='conf.py'): + freshenv=None, confname='conf.py', cleanenv=False): application.CONFIG_FILENAME = confname + self.cleanup_trees = [] + if srcdir is None: srcdir = test_root if srcdir == '(temp)': - tempdir = path(tempfile.mkdtemp()) / 'root' - test_root.copytree(tempdir) - srcdir = tempdir + tempdir = path(tempfile.mkdtemp()) + self.cleanup_trees.append(tempdir) + temproot = tempdir / 'root' + test_root.copytree(temproot) + srcdir = temproot else: srcdir = path(srcdir) self.builddir = srcdir.joinpath('_build') - if not self.builddir.isdir(): - self.builddir.makedirs() - self.made_builddir = True - else: - self.made_builddir = False if confdir is None: confdir = srcdir if outdir is None: outdir = srcdir.joinpath(self.builddir, buildername) if not outdir.isdir(): outdir.makedirs() + self.cleanup_trees.insert(0, outdir) if doctreedir is None: doctreedir = srcdir.joinpath(srcdir, self.builddir, 'doctrees') + if cleanenv: + self.cleanup_trees.insert(0, doctreedir) if confoverrides is None: confoverrides = {} if status is None: @@ -127,17 +129,14 @@ if warning is None: warning = ListOutput('stderr') if freshenv is None: - freshenv = True + freshenv = False application.Sphinx.__init__(self, srcdir, confdir, outdir, doctreedir, buildername, confoverrides, status, warning, freshenv) - def cleanup(self): - trees = [self.outdir, self.doctreedir] - if self.made_builddir: - trees.append(self.builddir) - for tree in trees: + def cleanup(self, doctrees=False): + for tree in self.cleanup_trees: shutil.rmtree(tree, True) From buildbot at python.org Tue Aug 5 00:31:27 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 22:31:27 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo 3.0 Message-ID: <20080804223128.185011E4006@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%203.0/builds/887 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1462, in write self.flush() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/home/buildbot/slave/py-build/3.0.norwitz-amd64/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 00:32:32 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 00:32:32 +0200 (CEST) Subject: [Python-checkins] r65531 - sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Message-ID: <20080804223232.9CB281E4006@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 00:32:32 2008 New Revision: 65531 Log: Marking load_extension as a private method now, since it is used only by EditorWindow; Fixed menu creation during extension loading in order to not recreate them; Fixed new bindings during extension loading in order to not miss any new tabs; Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Tue Aug 5 00:32:32 2008 @@ -508,34 +508,6 @@ # unless override: unregister from flist, terminate if last window self.close_hook() - def load_extension(self, name, tab): - load_only_once = ['CodeContext'] - if name in load_only_once and self.extensions.get(name, None): - return - - try: - mod = __import__(name, globals(), locals(), []) - except ImportError: - print "\nFailed to import extension: ", name - return - cls = getattr(mod, name) - keydefs = idleConf.GetExtensionBindings(name) - if hasattr(cls, "menudefs"): - self._fill_menus(cls.menudefs, keydefs) - ins = cls(tab.editpage) - self.extensions.setdefault(name, []).append(ins) - if keydefs: - self.apply_bindings(keydefs, tab) - for vevent in keydefs.keys(): - methodname = vevent.replace("-", "_") - while methodname[:1] == '<': - methodname = methodname[1:] - while methodname[-1:] == '>': - methodname = methodname[:-1] - methodname = methodname + "_event" - if hasattr(ins, methodname): - tab.editpage.text.bind(vevent, getattr(ins, methodname)) - def apply_bindings(self, keydefs=None, tab=None): if keydefs is None: keydefs = Bindings.default_keydefs @@ -620,7 +592,10 @@ indentsmall = indentlarge = 0 return indentlarge - indentsmall - # Private methods + # Private methods/attributes + + # extensions won't have more than one instance per window + _unique_extensions = ['CodeContext', 'ScriptBinding', 'FormatParagraph'] def _unload_extensions(self): for ins in self.extensions.values(): @@ -628,13 +603,50 @@ ins.close() self.extensions = {} + def _load_extension(self, name, tab): + ext_loaded = self.extensions.get(name, None) + + try: + mod = __import__(name, globals(), locals(), []) + except ImportError: + print "\nFailed to import extension: ", name + return + + keydefs = idleConf.GetExtensionBindings(name) + + if name not in self._unique_extensions or not ext_loaded: + # create a new instance + cls = getattr(mod, name) + ins = cls(tab.editpage) + self.extensions.setdefault(name, []).append(ins) + if not ext_loaded: + # create new items in menu only if this is the first time this + # extension is being loaded in this window + if hasattr(cls, "menudefs"): + self._fill_menus(cls.menudefs, keydefs) + elif name in self._unique_extensions and ext_loaded: + # get an existing instance + ins = self.extensions[name][0] + + if keydefs: + self.apply_bindings(keydefs, tab) + for vevent in keydefs.keys(): + methodname = vevent.replace("-", "_") + while methodname[:1] == '<': + methodname = methodname[1:] + while methodname[-1:] == '>': + methodname = methodname[:-1] + methodname = methodname + "_event" + if hasattr(ins, methodname): + tab.editpage.text.bind(vevent, getattr(ins, methodname)) + def _load_extensions(self): self._load_standard_extensions(self.text_notebook.last_page()) def _load_standard_extensions(self, tab): for name in self._get_standard_extension_names(): try: - self.load_extension(name, tab) + self._load_extension(name, tab) except: print "Failed to load extension", repr(name) traceback.print_exc() @@ -659,8 +671,10 @@ self.set_line_and_column() # update references in extensions that are loaded only once - if 'CodeContext' in self.extensions: - ext = self.extensions['CodeContext'][0] + for ext in self._unique_extensions: + if ext not in self.extensions: + continue + ext = self.extensions[ext][0] ext.editpage = curr_page def _create_statusbar(self): From python-checkins at python.org Tue Aug 5 00:35:08 2008 From: python-checkins at python.org (georg.brandl) Date: Tue, 5 Aug 2008 00:35:08 +0200 (CEST) Subject: [Python-checkins] r65532 - in doctools/branches/0.4.x: doc/markup/para.rst tests/root/contents.txt tests/root/markup.txt Message-ID: <20080804223508.976541E400F@bag.python.org> Author: georg.brandl Date: Tue Aug 5 00:35:07 2008 New Revision: 65532 Log: Add a test file for misc. markup and fix a doc bug. Added: doctools/branches/0.4.x/tests/root/markup.txt (contents, props changed) Modified: doctools/branches/0.4.x/doc/markup/para.rst doctools/branches/0.4.x/tests/root/contents.txt Modified: doctools/branches/0.4.x/doc/markup/para.rst ============================================================================== --- doctools/branches/0.4.x/doc/markup/para.rst (original) +++ doctools/branches/0.4.x/doc/markup/para.rst Tue Aug 5 00:35:07 2008 @@ -88,11 +88,9 @@ .. directive:: centered - This directive creates a centered boldfaced paragraph. Use it as follows:: + This directive creates a centered boldfaced line of text. Use it as follows:: - .. centered:: - - Paragraph contents. + .. centered:: LICENSE AGREEMENT Table-of-contents markup Modified: doctools/branches/0.4.x/tests/root/contents.txt ============================================================================== --- doctools/branches/0.4.x/tests/root/contents.txt (original) +++ doctools/branches/0.4.x/tests/root/contents.txt Tue Aug 5 00:35:07 2008 @@ -12,6 +12,7 @@ images includes + markup Indices and tables ================== Added: doctools/branches/0.4.x/tests/root/markup.txt ============================================================================== --- (empty file) +++ doctools/branches/0.4.x/tests/root/markup.txt Tue Aug 5 00:35:07 2008 @@ -0,0 +1,101 @@ +:tocdepth: 2 + +Testing various markup +====================== + +.. sectionauthor:: Georg Brandl + +.. contents:: TOC + + +Admonitions +----------- + +.. note:: Note + Note text. + +.. warning:: Warning + + Warning text. + +.. tip: + Tip text. + + +Tables +------ + +.. tabularcolumns:: |L|L|R| + ++----+----------------+----+ +| 1 | * Block elems | x | +| | * In table | | ++----+----------------+----+ +| 2 | Empty cells: | | ++----+----------------+----+ + + +Version markup +-------------- + +.. versionadded:: 0.5 + Some funny **stuff**. + +.. versionchanged:: 0.5 + Even more funny stuff. [#]_ + +.. deprecated:: 0.4 + Boring stuff. + + +Misc stuff +---------- + +.. seealso:: + + `Google `_ + For everything. + +.. rubric:: Side note + +This is a side note. + +.. centered:: LICENSE AGREEMENT + +.. acks:: + + * Terry Pratchett + * J. R. R. Tolkien + * Monty Python + +.. glossary:: + + boson + Particle with integer spin. + + fermion + Particle with half-integer spin. + +.. productionlist:: + try_stmt: try1_stmt | try2_stmt + try1_stmt: "try" ":" `suite` + : ("except" [`expression` ["," `target`]] ":" `suite`)+ + : ["else" ":" `suite`] + : ["finally" ":" `suite`] + try2_stmt: "try" ":" `suite` + : "finally" ":" `suite` + + +Index markup +------------ + +.. index:: + single: entry + pair: entry; pair + triple: index; entry; triple + + + +.. rubric:: Footnotes + +.. [#] Like footnotes. From python-checkins at python.org Tue Aug 5 00:36:48 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 00:36:48 +0200 (CEST) Subject: [Python-checkins] r65533 - sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Message-ID: <20080804223648.A1BB21E4006@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 00:36:48 2008 New Revision: 65533 Log: Forgot to update some method calls Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Tue Aug 5 00:36:48 2008 @@ -361,7 +361,7 @@ for event, keylist in keydefs.items(): text.event_delete(event, *keylist) - for extensionName in self.get_standard_extension_names(): + for extensionName in self._get_standard_extension_names(): xkeydefs = idleConf.GetExtensionBindings(extensionName) if xkeydefs: for page in self.text_notebook.pages.itervalues(): @@ -374,7 +374,7 @@ # Called from configDialog.py Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() self.apply_bindings() - for extensionName in self.get_standard_extension_names(): + for extensionName in self._get_standard_extension_names(): xkeydefs = idleConf.GetExtensionBindings(extensionName) if xkeydefs: self.apply_bindings(xkeydefs) From buildbot at python.org Tue Aug 5 00:41:42 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 22:41:42 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 3.0 Message-ID: <20080804224142.710AD1E400E@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%203.0/builds/103 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed svn sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 00:49:57 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 00:49:57 +0200 (CEST) Subject: [Python-checkins] r65534 - sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Message-ID: <20080804224957.401E71E4009@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 00:49:56 2008 New Revision: 65534 Log: Added a needed import; Made a except more explicit Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Tue Aug 5 00:49:56 2008 @@ -12,7 +12,7 @@ from code import InteractiveInterpreter try: - from Tkinter import Tk, TclError + from Tkinter import Tk, Toplevel, TclError from Tkconstants import END except ImportError: print>>sys.__stderr__, "** IDLE can't import Tkinter. " \ @@ -1189,7 +1189,7 @@ return self.interp.remote_stack_viewer() try: sys.last_traceback - except: + except AttributeError: tkMessageBox.showerror("No stack trace", "There is no stack trace yet.\n" "(sys.last_traceback is not defined)", From python-checkins at python.org Tue Aug 5 00:54:28 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 00:54:28 +0200 (CEST) Subject: [Python-checkins] r65535 - sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Message-ID: <20080804225428.AD0241E4006@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 00:54:28 2008 New Revision: 65535 Log: ranges[i] may be a textindex (Tcl Object), convert it to its string representation before continuing Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Tue Aug 5 00:54:28 2008 @@ -263,8 +263,8 @@ def ranges_to_linenumbers(self, ranges): lines = [] for index in range(0, len(ranges), 2): - lineno = int(float(ranges[index])) - end = int(float(ranges[index+1])) + lineno = int(float(str(ranges[index]))) + end = int(float(str(ranges[index+1]))) while lineno < end: lines.append(lineno) lineno += 1 From buildbot at python.org Tue Aug 5 00:57:29 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 22:57:29 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP 3.0 Message-ID: <20080804225729.BC3401E4006@bag.python.org> The Buildbot has detected a new failure of amd64 XP 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%203.0/builds/106 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed compile sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 01:39:53 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 23:39:53 +0000 Subject: [Python-checkins] buildbot failure in S-390 Debian 3.0 Message-ID: <20080804233954.137621E4006@bag.python.org> The Buildbot has detected a new failure of S-390 Debian 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/S-390%20Debian%203.0/builds/690 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-s390 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/home/pybot/buildarea/3.0.klose-debian-s390/build/Lib/io.py", line 1462, in write self.flush() File "/home/pybot/buildarea/3.0.klose-debian-s390/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/home/pybot/buildarea/3.0.klose-debian-s390/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 01:42:28 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 23:42:28 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 trunk Message-ID: <20080804234228.5D1331E4006@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%20trunk/builds/3777 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon,gregory.p.smith,nick.coghlan BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 01:50:37 2008 From: buildbot at python.org (buildbot at python.org) Date: Mon, 04 Aug 2008 23:50:37 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP trunk Message-ID: <20080804235037.92F0B1E4007@bag.python.org> The Buildbot has detected a new failure of amd64 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%20trunk/builds/118 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: brett.cannon,mark.dickinson BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 02:40:58 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 00:40:58 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 3.0 Message-ID: <20080805004058.7CC171E4007@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 3.0. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%203.0/builds/1199 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch branches/py3k] HEAD Blamelist: brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: Traceback (most recent call last): File "./Lib/test/regrtest.py", line 1197, in main() File "./Lib/test/regrtest.py", line 400, in main print(test) File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1462, in write self.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1427, in flush self.buffer.flush() File "/Users/buildslave/bb/3.0.psf-g4/build/Lib/io.py", line 1054, in flush n = self.raw.write(self._write_buf) IOError: [Errno 9] Bad file descriptor make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 03:00:57 2008 From: python-checkins at python.org (andrew.kuchling) Date: Tue, 5 Aug 2008 03:00:57 +0200 (CEST) Subject: [Python-checkins] r65536 - python/trunk/Lib/test/test_mailbox.py Message-ID: <20080805010057.A40A61E4006@bag.python.org> Author: andrew.kuchling Date: Tue Aug 5 03:00:57 2008 New Revision: 65536 Log: Bug 3228: take a test from Niels Gustaebel's patch, and based on his patch, check for having os.stat available Modified: python/trunk/Lib/test/test_mailbox.py Modified: python/trunk/Lib/test/test_mailbox.py ============================================================================== --- python/trunk/Lib/test/test_mailbox.py (original) +++ python/trunk/Lib/test/test_mailbox.py Tue Aug 5 03:00:57 2008 @@ -716,10 +716,32 @@ for msg in self._box: pass - def test_file_perms(self): + def test_file_permissions(self): + # Verify that message files are created without execute permissions + if not hasattr(os, "stat") or not hasattr(os, "umask"): + return + msg = mailbox.MaildirMessage(self._template % 0) + orig_umask = os.umask(0) + try: + key = self._box.add(msg) + finally: + os.umask(orig_umask) + path = os.path.join(self._path, self._box._lookup(key)) + mode = os.stat(path).st_mode + self.assert_(mode & 0111 == 0) + + def test_folder_file_perms(self): # From bug #3228, we want to verify that the file created inside a Maildir # subfolder isn't marked as executable. - subfolder = self._box.add_folder('subfolder') + if not hasattr(os, "stat") or not hasattr(os, "umask"): + return + + orig_umask = os.umask(0) + try: + subfolder = self._box.add_folder('subfolder') + finally: + os.umask(orig_umask) + path = os.path.join(subfolder._path, 'maildirfolder') st = os.stat(path) perms = st.st_mode @@ -823,7 +845,7 @@ # From bug #3228, we want to verify that the mailbox file isn't executable, # even if the umask is set to something that would leave executable bits set. # We only run this test on platforms that support umask. - if hasattr(os, 'umask'): + if hasattr(os, 'umask') and hasattr(os, 'stat'): try: old_umask = os.umask(0077) self._box.close() @@ -831,12 +853,13 @@ self._box = mailbox.mbox(self._path, create=True) self._box.add('') self._box.close() - st = os.stat(self._path) - perms = st.st_mode - self.assertFalse((perms & 0111)) # Execute bits should all be off. finally: os.umask(old_umask) + st = os.stat(self._path) + perms = st.st_mode + self.assertFalse((perms & 0111)) # Execute bits should all be off. + class TestMMDF(_TestMboxMMDF): _factory = lambda self, path, factory=None: mailbox.MMDF(path, factory) From python-checkins at python.org Tue Aug 5 03:17:06 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 03:17:06 +0200 (CEST) Subject: [Python-checkins] r65537 - in sandbox/trunk/ttk-gsoc/src/idlelib: EditorWindow.py FileList.py IOBinding.py PyShell.py UndoDelegator.py editorpage.py Message-ID: <20080805011706.87D371E4006@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 03:17:06 2008 New Revision: 65537 Log: UndoDelegator: will update window's title only if the tab in question has been marked as initialized. PyShell: I'm considering PyShell window too special and thus it will show like a non-tabbed window; Moved IOBinding imports to the top. EditorWindow: minor touches to update window's/tab's title correctly. editorpage: close_tab method no longer tries to save a PyShell tab; PyShell tab is being considered special enough to have its window and thus modules and files should open at a new window when requesting such actions from a PyShell tab; minor touches to update window's/tab's title correctly. IOBinding: Force open on a new window if action is coming from a PyShell tab. FileList: minor touch to update window's title correctly. Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py sandbox/trunk/ttk-gsoc/src/idlelib/UndoDelegator.py sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/EditorWindow.py Tue Aug 5 03:17:06 2008 @@ -227,9 +227,11 @@ page.editpage = EditorPage(page.frame, self, title=page_title, name='text', padx=5, wrap='none') + firstpage = False # don't update window's title if self.menudict is None: # This EditorWindow is being created now, perform the following # tasks before. + firstpage = True # will cause window's title to be updated self.menudict = {} self._createmenubar(page.editpage.text) # Create the recent files submenu @@ -238,7 +240,7 @@ underline=0, menu=self.recent_files_menu) self.update_recent_files_list() - page.editpage.post_init(filename=filename) + page.editpage.post_init(filename=filename, update_window_title=firstpage) text = page.editpage.text vbar['command'] = text.yview @@ -480,7 +482,6 @@ self.close() def close(self): - # XXX need to skip a possible PyShell tab to_check = self.text_notebook.pages.copy() while to_check: @@ -666,7 +667,7 @@ return self.text = curr_page.text - curr_page.saved_change_hook(update_tab_title=False) # update window title + curr_page.saved_change_hook(True, False) # update window title curr_page.text.focus_set() self.set_line_and_column() Modified: sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/FileList.py Tue Aug 5 03:17:06 2008 @@ -72,7 +72,7 @@ self.root.quit() def filename_changed_edit(self, page, editwin): - page.saved_change_hook() + page.saved_change_hook(page.tab_initialized) try: key = self.inversedict[editwin] except KeyError: Modified: sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/IOBinding.py Tue Aug 5 03:17:06 2008 @@ -221,7 +221,12 @@ else: if idleConf.GetOption('main', 'EditorWindow', 'file-in-tab', default=1, type='bool'): - action = self.editwin.new_tab + if interp: + # this is a PyShell, force file to be opened in a + # new window + action = None + else: + action = self.editwin.new_tab else: action = None self.editwin.flist.open(filename, action) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Tue Aug 5 03:17:06 2008 @@ -38,6 +38,7 @@ import utils import idlever import Debugger +import IOBinding import RemoteDebugger from FileList import FileList from OutputWindow import OutputWindow @@ -606,7 +607,6 @@ self.save_warnings_filters = warnings.filters[:] warnings.filterwarnings(action="error", category=SyntaxWarning) if isinstance(source, types.UnicodeType): - import IOBinding try: source = source.encode(IOBinding.encoding) except UnicodeError: @@ -831,6 +831,33 @@ flist = PyShellFileList(root) # OutputWindow.__init__(self, flist, None, None) + # remove things related to tabs. The window running the shell is + # considered special enough to have a single window for it. + + # remove tabs area + if TTK: + self.text_notebook['style'] = 'PyShell.TNotebook' + style = Style(self.top) + style.layout('PyShell.TNotebook.Tab', [('null', '')]) + else: + self.text_notebook._tab_set.grid_forget() + + # remove commands related to tab + if 'file' in self.menudict: + menu = self.menudict['file'] + curr_entry = None + i = 0 + while True: + last_entry, curr_entry = curr_entry, menu.entryconfigure(i) + if last_entry == curr_entry: + break + + if 'label' in curr_entry and 'Tab' in curr_entry['label'][-1]: + menu.delete(i) + i += 1 + self.text.unbind('<>') + self.text.unbind('<>') + # self.usetabs = True # indentwidth must be 8 when using tabs. See note in EditorWindow: @@ -844,7 +871,6 @@ self.save_stdout = sys.stdout self.save_stderr = sys.stderr self.save_stdin = sys.stdin - import IOBinding self.stdout = PseudoFile(self, "stdout", IOBinding.encoding) self.stderr = PseudoFile(self, "stderr", IOBinding.encoding) self.console = PseudoFile(self, "console", IOBinding.encoding) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/UndoDelegator.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/UndoDelegator.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/UndoDelegator.py Tue Aug 5 03:17:06 2008 @@ -74,7 +74,7 @@ if is_saved != self.was_saved: self.was_saved = is_saved if self.saved_change_hook: - self.saved_change_hook() + self.saved_change_hook(None) def insert(self, index, chars, tags=None): self.addcmd(InsertCommand(index, chars, tags)) Modified: sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/editorpage.py Tue Aug 5 03:17:06 2008 @@ -76,6 +76,7 @@ def __init__(self, parent_frame, editwin, title=None, **kwargs): self.editwin = editwin self.title = title + self.tab_initialized = False kwargs.setdefault('width', idleConf.GetOption('main', 'EditorPage', 'width')) kwargs.setdefault('height', idleConf.GetOption('main', 'EditorPage', @@ -95,17 +96,22 @@ self.reset_colorizer() self._setup_bindings() - def post_init(self, filename=None): + def post_init(self, filename=None, update_window_title=False): if filename: if os.path.exists(filename) and not os.path.isdir(filename): self.io.loadfile(filename) else: self.io.set_filename(filename) - self.saved_change_hook(False) + self.saved_change_hook(update_window_title=update_window_title) + self.tab_initialized = True def close_tab(self, event=None): """Close current tab, if no more tabs present, close the window.""" - reply = str(self.maybesave()) + if hasattr(self.editwin, 'interp'): + # this is a PyShell, don't ask to save + reply = 'yes' + else: + reply = str(self.maybesave()) if reply != "cancel": if self.io.filename: self.editwin.update_recent_files_list(new_file=self.io.filename) @@ -131,7 +137,7 @@ self.text = None # XXX (1) mark where these functions are used - def saved_change_hook(self, update_window_title=True, update_tab_title=True): + def saved_change_hook(self,update_window_title=False,update_tab_title=True): short = self.editwin.short_title() long = self.long_title() @@ -154,7 +160,8 @@ if update_tab_title: self.editwin.text_notebook.update_tabtitle(self, tabtitle) - if update_window_title: + if update_window_title or ( + update_window_title is None and self.tab_initialized): self.editwin.top.wm_title(title) self.editwin.top.wm_iconname(icon) @@ -167,7 +174,7 @@ def filename_change_hook(self): if self.editwin.flist: self.editwin.flist.filename_changed_edit(self, self.editwin) - self.saved_change_hook() + self.saved_change_hook(self.tab_initialized) self.editwin.top.update_windowlist_registry(self.editwin) self.reset_colorizer() @@ -468,7 +475,11 @@ if self.editwin.flist: if idleConf.GetOption('main', 'EditorWindow', 'file-in-tab', default=1, type='bool'): - action = self.editwin.new_tab + if hasattr(self.editwin, 'interp'): + # PyShell window must open a module in a new window + action = None + else: + action = self.editwin.new_tab else: action = None self.editwin.flist.open(file, action) From buildbot at python.org Tue Aug 5 03:21:21 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 01:21:21 +0000 Subject: [Python-checkins] buildbot failure in x86 XP-3 trunk Message-ID: <20080805012121.B9F911E4007@bag.python.org> The Buildbot has detected a new failure of x86 XP-3 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20XP-3%20trunk/builds/102 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling,brett.cannon BUILD FAILED: failed svn sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 03:25:40 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 03:25:40 +0200 (CEST) Subject: [Python-checkins] r65538 - sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Message-ID: <20080805012540.DFEFA1E4007@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 03:25:40 2008 New Revision: 65538 Log: I was already missing the possibility to close PyShell with close-tab hotkey.. added it back Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Tue Aug 5 03:25:40 2008 @@ -853,10 +853,11 @@ break if 'label' in curr_entry and 'Tab' in curr_entry['label'][-1]: - menu.delete(i) + if 'New' in ' '.join(curr_entry['label'][-1]): + menu.delete(i) i += 1 self.text.unbind('<>') - self.text.unbind('<>') + #self.text.unbind('<>') # self.usetabs = True From python-checkins at python.org Tue Aug 5 03:38:08 2008 From: python-checkins at python.org (andrew.kuchling) Date: Tue, 5 Aug 2008 03:38:08 +0200 (CEST) Subject: [Python-checkins] r65539 - python/trunk/Parser/tokenizer.c Message-ID: <20080805013808.61BCD1E4007@bag.python.org> Author: andrew.kuchling Date: Tue Aug 5 03:38:08 2008 New Revision: 65539 Log: #3367 from Kristjan Valur Jonsson: If a PyTokenizer_FromString() is called with an empty string, the tokenizer's line_start member never gets initialized. Later, it is compared with the token pointer 'a' in parsetok.c:193 and that behavior can result in undefined behavior. Modified: python/trunk/Parser/tokenizer.c Modified: python/trunk/Parser/tokenizer.c ============================================================================== --- python/trunk/Parser/tokenizer.c (original) +++ python/trunk/Parser/tokenizer.c Tue Aug 5 03:38:08 2008 @@ -1117,7 +1117,7 @@ register int c; int blankline; - *p_start = *p_end = NULL; + tok->line_start = *p_start = *p_end = NULL; nextline: tok->start = NULL; blankline = 0; From python-checkins at python.org Tue Aug 5 03:40:52 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 03:40:52 +0200 (CEST) Subject: [Python-checkins] r65540 - sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Message-ID: <20080805014053.039A51E4007@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 03:40:52 2008 New Revision: 65540 Log: Removed a not really interesting comment Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/PyShell.py Tue Aug 5 03:40:52 2008 @@ -1444,7 +1444,7 @@ if enable_edit: if not (cmd or script): for filename in args: - flist.open(filename) # XXX this creates a new EditorWindow + flist.open(filename) if not args: flist.new() if enable_shell: From buildbot at python.org Tue Aug 5 03:47:39 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 01:47:39 +0000 Subject: [Python-checkins] buildbot failure in amd64 gentoo trunk Message-ID: <20080805014739.7E1AB1E4007@bag.python.org> The Buildbot has detected a new failure of amd64 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20gentoo%20trunk/builds/1273 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling BUILD FAILED: failed test Excerpt from the test logfile: make: *** [buildbottest] Segmentation fault (core dumped) sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 03:54:07 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 03:54:07 +0200 (CEST) Subject: [Python-checkins] r65541 - sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py Message-ID: <20080805015407.AB8C41E4019@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 03:54:07 2008 New Revision: 65541 Log: Call set_theme only if Ttk is being used Modified: sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py Modified: sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py ============================================================================== --- sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py (original) +++ sandbox/trunk/ttk-gsoc/src/idlelib/configDialog.py Tue Aug 5 03:54:07 2008 @@ -1255,7 +1255,8 @@ instance.set_notabs_indentwidth() instance.ApplyKeybindings() instance.reset_help_menu_entries() - instance.set_theme(self.ttkstyle) + if TTK: + instance.set_theme(self.ttkstyle) def Cancel(self): self.destroy() From python-checkins at python.org Tue Aug 5 04:02:53 2008 From: python-checkins at python.org (guilherme.polo) Date: Tue, 5 Aug 2008 04:02:53 +0200 (CEST) Subject: [Python-checkins] r65542 - sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff Message-ID: <20080805020253.D3A771E4007@bag.python.org> Author: guilherme.polo Date: Tue Aug 5 04:02:52 2008 New Revision: 65542 Log: Current patch for adding tabs support, ttk support and other things to the idlelib Added: sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff Added: sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff ============================================================================== --- (empty file) +++ sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff Tue Aug 5 04:02:52 2008 @@ -0,0 +1,8367 @@ +Index: ToolTip.py +=================================================================== +--- ToolTip.py (revision 63995) ++++ ToolTip.py (revision 65541) +@@ -1,89 +0,0 @@ +-# general purpose 'tooltip' routines - currently unused in idlefork +-# (although the 'calltips' extension is partly based on this code) +-# may be useful for some purposes in (or almost in ;) the current project scope +-# Ideas gleaned from PySol +- +-from Tkinter import * +- +-class ToolTipBase: +- +- def __init__(self, button): +- self.button = button +- self.tipwindow = None +- self.id = None +- self.x = self.y = 0 +- self._id1 = self.button.bind("", self.enter) +- self._id2 = self.button.bind("", self.leave) +- self._id3 = self.button.bind("", self.leave) +- +- def enter(self, event=None): +- self.schedule() +- +- def leave(self, event=None): +- self.unschedule() +- self.hidetip() +- +- def schedule(self): +- self.unschedule() +- self.id = self.button.after(1500, self.showtip) +- +- def unschedule(self): +- id = self.id +- self.id = None +- if id: +- self.button.after_cancel(id) +- +- def showtip(self): +- if self.tipwindow: +- return +- # The tip window must be completely outside the button; +- # otherwise when the mouse enters the tip window we get +- # a leave event and it disappears, and then we get an enter +- # event and it reappears, and so on forever :-( +- x = self.button.winfo_rootx() + 20 +- y = self.button.winfo_rooty() + self.button.winfo_height() + 1 +- self.tipwindow = tw = Toplevel(self.button) +- tw.wm_overrideredirect(1) +- tw.wm_geometry("+%d+%d" % (x, y)) +- self.showcontents() +- +- def showcontents(self, text="Your text here"): +- # Override this in derived class +- label = Label(self.tipwindow, text=text, justify=LEFT, +- background="#ffffe0", relief=SOLID, borderwidth=1) +- label.pack() +- +- def hidetip(self): +- tw = self.tipwindow +- self.tipwindow = None +- if tw: +- tw.destroy() +- +-class ToolTip(ToolTipBase): +- def __init__(self, button, text): +- ToolTipBase.__init__(self, button) +- self.text = text +- def showcontents(self): +- ToolTipBase.showcontents(self, self.text) +- +-class ListboxToolTip(ToolTipBase): +- def __init__(self, button, items): +- ToolTipBase.__init__(self, button) +- self.items = items +- def showcontents(self): +- listbox = Listbox(self.tipwindow, background="#ffffe0") +- listbox.pack() +- for item in self.items: +- listbox.insert(END, item) +- +-def main(): +- # Test code +- root = Tk() +- b = Button(root, text="Hello", command=root.destroy) +- b.pack() +- root.update() +- tip = ListboxToolTip(b, ["Hello", "world"]) +- root.mainloop() +- +-if __name__ == '__main__': +- main() +Index: AutoCompleteWindow.py +=================================================================== +--- AutoCompleteWindow.py (revision 63995) ++++ AutoCompleteWindow.py (revision 65541) +@@ -1,10 +1,16 @@ + """ + An auto-completion window for IDLE, used by the AutoComplete extension + """ +-from Tkinter import * ++from Tkinter import Toplevel, Listbox, Scrollbar, TclError ++from Tkconstants import END, LEFT, RIGHT, BOTH, VERTICAL, Y ++ ++import AutoComplete + from MultiCall import MC_SHIFT +-import AutoComplete ++from configHandler import idleConf + ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Scrollbar ++ + HIDE_VIRTUAL_EVENT_NAME = "<>" + HIDE_SEQUENCES = ("", "") + KEYPRESS_VIRTUAL_EVENT_NAME = "<>" +Index: UndoDelegator.py +=================================================================== +--- UndoDelegator.py (revision 63995) ++++ UndoDelegator.py (revision 65541) +@@ -1,5 +1,5 @@ + import string +-from Tkinter import * ++ + from Delegator import Delegator + + #$ event <> +@@ -74,7 +74,7 @@ + if is_saved != self.was_saved: + self.was_saved = is_saved + if self.saved_change_hook: +- self.saved_change_hook() ++ self.saved_change_hook(None) + + def insert(self, index, chars, tags=None): + self.addcmd(InsertCommand(index, chars, tags)) +@@ -336,6 +336,7 @@ + return self.depth + + def main(): ++ from Tkinter import Tk, Text + from Percolator import Percolator + root = Tk() + root.wm_protocol("WM_DELETE_WINDOW", root.quit) +Index: Bindings.py +=================================================================== +--- Bindings.py (revision 63995) ++++ Bindings.py (revision 65541) +@@ -6,15 +6,16 @@ + makes it possible, for example, to define a Debug menu which is only present in + the PythonShell window, and a Format menu which is only present in the Editor + windows. +- + """ + import sys ++ + from configHandler import idleConf + + menudefs = [ + # underscore prefixes character to underscore + ('file', [ + ('_New Window', '<>'), ++ ('New _Tab', '<>'), + ('_Open...', '<>'), + ('Open _Module...', '<>'), + ('Class _Browser', '<>'), +@@ -26,6 +27,7 @@ + None, + ('Prin_t Window', '<>'), + None, ++ ('Close Tab', '<>'), + ('_Close', '<>'), + ('E_xit', '<>'), + ]), +@@ -80,7 +82,6 @@ + ]), + ] + +-import sys + if sys.platform == 'darwin' and '.app' in sys.executable: + # Running as a proper MacOS application bundle. This block restructures + # the menus a little to make them conform better to the HIG. +Index: AutoComplete.py +=================================================================== +--- AutoComplete.py (revision 63995) ++++ AutoComplete.py (revision 65541) +@@ -38,11 +38,11 @@ + popupwait = idleConf.GetOption("extensions", "AutoComplete", + "popupwait", type="int", default=0) + +- def __init__(self, editwin=None): +- self.editwin = editwin +- if editwin is None: # subprocess and test ++ def __init__(self, editpage=None): ++ self.editpage = editpage ++ if editpage is None: # subprocess and test + return +- self.text = editwin.text ++ self.text = editpage.text + self.autocompletewindow = None + + # id of delayed call, and the index of the text insert when the delayed +@@ -120,7 +120,7 @@ + self.text.after_cancel(self._delayed_completion_id) + self._delayed_completion_id = None + +- hp = HyperParser(self.editwin, "insert") ++ hp = HyperParser(self.editpage, "insert") + curline = self.text.get("insert linestart", "insert") + i = j = len(curline) + if hp.is_in_string() and (not mode or mode==COMPLETE_FILES): +@@ -176,7 +176,7 @@ + module may be inoperative if the module was not the last to run. + """ + try: +- rpcclt = self.editwin.flist.pyshell.interp.rpcclt ++ rpcclt = self.editpage.editwin.flist.pyshell.interp.rpcclt + except: + rpcclt = None + if rpcclt: +Index: configHandler.py +=================================================================== +--- configHandler.py (revision 63995) ++++ configHandler.py (revision 65541) +@@ -19,10 +19,10 @@ + """ + import os + import sys +-import string +-import macosxSupport + from ConfigParser import ConfigParser, NoOptionError, NoSectionError + ++import macosxSupport ++ + class InvalidConfigType(Exception): pass + class InvalidConfigSet(Exception): pass + class InvalidFgBg(Exception): pass +@@ -295,7 +295,10 @@ + back=themeDict['normal-background'] + else: + back=themeDict[element+'-background'] +- highlight={"foreground": fore,"background": back} ++ #bold = 0 ++ #if element + '-bold' in themeDict: ++ # bold = themeDict[element + '-bold'] ++ highlight={"foreground": fore, "background": back}#, "font": bold} + if not fgBg: #return dict of both colours + return highlight + else: #return specified colour only +@@ -558,6 +561,10 @@ + defined here. + """ + keyBindings={ ++ '<>': [''], ++ '<>': [''], ++ '<>': ['>': ['>': ['', ''], + '<>': ['', ''], + '<>': ['', ''], +@@ -649,7 +656,7 @@ + menuItem='' #make these empty + helpPath='' #so value won't be added to list + else: #config entry contains ';' as expected +- value=string.split(value,';') ++ value = value.split(';') + menuItem=value[0].strip() + helpPath=value[1].strip() + if menuItem and helpPath: #neither are empty strings +Index: HyperParser.py +=================================================================== +--- HyperParser.py (revision 63995) ++++ HyperParser.py (revision 65541) +@@ -10,22 +10,22 @@ + + import string + import keyword ++ + import PyParse ++from editorpage import index2line + + class HyperParser: + +- def __init__(self, editwin, index): ++ def __init__(self, editpage, index): + """Initialize the HyperParser to analyze the surroundings of the given + index. + """ + +- self.editwin = editwin +- self.text = text = editwin.text ++ self.editwin = editwin = editpage.editwin ++ self.text = text = editpage.text + + parser = PyParse.Parser(editwin.indentwidth, editwin.tabwidth) + +- def index2line(index): +- return int(float(index)) + lno = index2line(text.index(index)) + + if not editwin.context_use_ps1: +@@ -38,7 +38,7 @@ + # its status will be the same as the char before it, if should. + parser.set_str(text.get(startatindex, stopatindex)+' \n') + bod = parser.find_good_parse_start( +- editwin._build_char_in_string_func(startatindex)) ++ editwin.build_char_in_string_func(startatindex)) + if bod is not None or startat == 1: + break + parser.set_lo(bod or 0) +Index: ColorDelegator.py +=================================================================== +--- ColorDelegator.py (revision 63995) ++++ ColorDelegator.py (revision 65541) +@@ -1,8 +1,8 @@ ++import re + import time +-import re + import keyword + import __builtin__ +-from Tkinter import * ++ + from Delegator import Delegator + from configHandler import idleConf + +@@ -182,8 +182,11 @@ + ok = False + while not ok: + mark = next +- next = self.index(mark + "+%d lines linestart" % +- lines_to_get) ++ # XXX self could be None here in some cases. ++ # I can reproduce it by clicking "Apply" then "Ok" ++ # quickly in the config dialog (while editing a not so ++ # small file). ++ next = self.index(mark + "+%d lines linestart" % lines_to_get) + lines_to_get = min(lines_to_get * 2, 100) + ok = "SYNC" in self.tag_names(next + "-1c") + line = self.get(mark, next) +@@ -248,6 +251,7 @@ + self.tag_remove(tag, "1.0", "end") + + def main(): ++ from Tkinter import Tk, Text + from Percolator import Percolator + root = Tk() + root.wm_protocol("WM_DELETE_WINDOW", root.quit) +Index: configSectionNameDialog.py +=================================================================== +--- configSectionNameDialog.py (revision 63995) ++++ configSectionNameDialog.py (revision 65541) +@@ -2,96 +2,111 @@ + Dialog that allows user to specify a new config file section name. + Used to get new highlight theme and keybinding set names. + """ +-from Tkinter import * ++from Tkinter import Toplevel, Entry, Frame, Label, Button, StringVar ++from Tkconstants import TOP, BOTTOM, RIGHT, BOTH, SUNKEN, X + import tkMessageBox + ++from configHandler import idleConf ++ ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Entry, Frame, Label, Button ++ + class GetCfgSectionNameDialog(Toplevel): +- def __init__(self,parent,title,message,usedNames): ++ def __init__(self, parent, title, message, usedNames): + """ + message - string, informational message to display + usedNames - list, list of names already in use for validity check + """ + Toplevel.__init__(self, parent) + self.configure(borderwidth=5) +- self.resizable(height=FALSE,width=FALSE) ++ self.resizable(height=False, width=False) + self.title(title) + self.transient(parent) + self.grab_set() + self.protocol("WM_DELETE_WINDOW", self.Cancel) + self.parent = parent +- self.message=message +- self.usedNames=usedNames +- self.result='' ++ self.message = message ++ self.usedNames = usedNames ++ self.result = '' + self.CreateWidgets() + self.withdraw() #hide while setting geometry + self.update_idletasks() +- #needs to be done here so that the winfo_reqwidth is valid +- self.messageInfo.config(width=self.frameMain.winfo_reqwidth()) + self.geometry("+%d+%d" % +- ((parent.winfo_rootx()+((parent.winfo_width()/2) +- -(self.winfo_reqwidth()/2)), +- parent.winfo_rooty()+((parent.winfo_height()/2) +- -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent ++ ((parent.winfo_rootx() + ((parent.winfo_width() / 2) ++ - (self.winfo_reqwidth() / 2)), ++ parent.winfo_rooty() + ((parent.winfo_height() / 2) ++ - (self.winfo_reqheight() / 2)) )) ) #centre dialog over parent + self.deiconify() #geometry set, unhide + self.wait_window() + + def CreateWidgets(self): +- self.name=StringVar(self) +- self.fontSize=StringVar(self) +- self.frameMain = Frame(self,borderwidth=2,relief=SUNKEN) +- self.frameMain.pack(side=TOP,expand=TRUE,fill=BOTH) +- self.messageInfo=Message(self.frameMain,anchor=W,justify=LEFT,padx=5,pady=5, +- text=self.message)#,aspect=200) +- entryName=Entry(self.frameMain,textvariable=self.name,width=30) ++ self.name = StringVar(self) ++ self.fontSize = StringVar(self) ++ self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN) ++ self.messageInfo = Label(self.frameMain, text=self.message) ++ entryName = Entry(self.frameMain, textvariable=self.name, width=30) ++ frameButtons = Frame(self) ++ self.buttonOk = Button(frameButtons, text='Ok', command=self.Ok) ++ self.buttonCancel = Button(frameButtons, text='Cancel', ++ command=self.Cancel) ++ + entryName.focus_set() +- self.messageInfo.pack(padx=5,pady=5)#,expand=TRUE,fill=BOTH) +- entryName.pack(padx=5,pady=5) +- frameButtons=Frame(self) +- frameButtons.pack(side=BOTTOM,fill=X) +- self.buttonOk = Button(frameButtons,text='Ok', +- width=8,command=self.Ok) +- self.buttonOk.grid(row=0,column=0,padx=5,pady=5) +- self.buttonCancel = Button(frameButtons,text='Cancel', +- width=8,command=self.Cancel) +- self.buttonCancel.grid(row=0,column=1,padx=5,pady=5) + ++ self.frameMain.pack(side=TOP, expand=True, fill=BOTH) ++ self.messageInfo.pack(padx=5, pady=5) ++ entryName.pack(padx=5, pady=5) ++ frameButtons.pack(side=BOTTOM, fill=X) ++ self.buttonOk.pack(padx=1, pady=5, side=RIGHT) ++ self.buttonCancel.pack(pady=5, padx=5, side=RIGHT) ++ ++ if TTK: ++ self.messageInfo['padding'] = 5 ++ frameButtons['style'] = 'RootColor.TFrame' ++ else: ++ self.messageInfo.configure(padx=5, pady=5) ++ + def NameOk(self): + #simple validity check for a sensible + #ConfigParser file section name +- nameOk=1 +- name=self.name.get() ++ nameOk = 1 ++ name = self.name.get() + name.strip() ++ + if not name: #no name specified + tkMessageBox.showerror(title='Name Error', +- message='No name specified.', parent=self) +- nameOk=0 +- elif len(name)>30: #name too long ++ message='No name specified.', parent=self) ++ nameOk = 0 ++ elif len(name) > 30: #name too long + tkMessageBox.showerror(title='Name Error', +- message='Name too long. It should be no more than '+ +- '30 characters.', parent=self) ++ message=('Name too long. It should be no more than ' ++ '30 characters.'), parent=self) + nameOk=0 + elif name in self.usedNames: + tkMessageBox.showerror(title='Name Error', + message='This name is already in use.', parent=self) + nameOk=0 ++ + return nameOk + + def Ok(self, event=None): + if self.NameOk(): +- self.result=self.name.get().strip() ++ self.result = self.name.get().strip() + self.destroy() + + def Cancel(self, event=None): +- self.result='' ++ self.result = '' + self.destroy() + + if __name__ == '__main__': ++ from Tkinter import Tk + #test the dialog +- root=Tk() + def run(): +- keySeq='' +- dlg=GetCfgSectionNameDialog(root,'Get Name', +- 'The information here should need to be word wrapped. Test.') ++ keySeq = '' ++ dlg = GetCfgSectionNameDialog(root, 'Get Name', ++ 'The information here should need to be word wrapped. Test.', []) + print dlg.result +- Button(root,text='Dialog',command=run).pack() ++ ++ root=Tk() ++ Button(root, text='Dialog', command=run).pack() + root.mainloop() +Index: stylist.py +=================================================================== +--- stylist.py (revision 0) ++++ stylist.py (revision 65541) +@@ -0,0 +1,29 @@ ++from configHandler import idleConf ++ ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++ ++class PoorManStyle(object): ++ def __init__(self, parent, styles=None, cfgstyles=None): ++ self.parent = parent ++ self.cfgstyles = cfgstyles ++ self.styles = styles ++ ++ def configure(self, style, lookup=None, background=None): ++ if style not in self.cfgstyles: # passed wrong style probably ++ return ++ ++ widget = getattr(self.parent, self.cfgstyles[style]) ++ if lookup: ++ return widget.cget('bg') ++ ++ widget.configure(bg=background) ++ ++ def style_it(self, w, style): ++ if TTK: ++ w['style'] = style ++ return ++ ++ if not style in self.styles: # may not need to be styled ++ return ++ ++ w.configure(**self.styles[style]) +Index: ZoomHeight.py +=================================================================== +--- ZoomHeight.py (revision 63995) ++++ ZoomHeight.py (revision 65541) +@@ -12,11 +12,11 @@ + ]) + ] + +- def __init__(self, editwin): +- self.editwin = editwin ++ def __init__(self, editpage): ++ self.editpage = editpage + + def zoom_height_event(self, event): +- top = self.editwin.top ++ top = self.editpage.editwin.top + zoom_height(top) + + def zoom_height(top): +Index: PyShell.py +=================================================================== +--- PyShell.py (revision 63995) ++++ PyShell.py (revision 65541) +@@ -1,40 +1,50 @@ + #! /usr/bin/env python +- + import os +-import os.path ++import re + import sys ++import time ++import types + import string + import getopt +-import re + import socket +-import time ++import linecache + import threading +-import traceback +-import types +-import macosxSupport +- +-import linecache + from code import InteractiveInterpreter + + try: +- from Tkinter import * ++ from Tkinter import Tk, Toplevel, TclError ++ from Tkconstants import END + except ImportError: + print>>sys.__stderr__, "** IDLE can't import Tkinter. " \ + "Your Python may not be configured for Tk. **" + sys.exit(1) ++try: ++ from ttk import Style ++ TTK = 1 ++except ImportError: ++ print >> sys.stderr, "** IDLE can't import ttk." ++ TTK = 0 ++ + import tkMessageBox + +-from EditorWindow import EditorWindow, fixwordbreaks +-from FileList import FileList +-from ColorDelegator import ColorDelegator +-from UndoDelegator import UndoDelegator +-from OutputWindow import OutputWindow ++import macosxSupport + from configHandler import idleConf +-import idlever + ++# store ttk availability ++idleConf.SetOption('main', 'General', 'use-ttk', str(TTK)) ++idleConf.SaveUserCfgFiles() ++ + import rpc ++import utils ++import idlever + import Debugger ++import IOBinding + import RemoteDebugger ++from FileList import FileList ++from OutputWindow import OutputWindow ++from EditorWindow import EditorWindow, fixwordbreaks ++from UndoDelegator import UndoDelegator ++from ColorDelegator import ColorDelegator + + IDENTCHARS = string.ascii_letters + string.digits + "_" + LOCALHOST = '127.0.0.1' +@@ -55,18 +65,20 @@ + except ImportError: + pass + else: +- def idle_showwarning(message, category, filename, lineno): ++ def idle_showwarning(message, category, filename, lineno, line=None): + file = warning_stream + try: +- file.write(warnings.formatwarning(message, category, filename, lineno)) ++ file.write(warnings.formatwarning(message, category, filename, ++ lineno, line)) + except IOError: + pass ## file (probably __stderr__) is invalid, warning dropped. + warnings.showwarning = idle_showwarning +- def idle_formatwarning(message, category, filename, lineno): ++ def idle_formatwarning(message, category, filename, lineno, line=None): + """Format warnings the IDLE way""" + s = "\nWarning (from warnings module):\n" + s += ' File \"%s\", line %s\n' % (filename, lineno) +- line = linecache.getline(filename, lineno).strip() ++ if line is None: ++ line = linecache.getline(filename, lineno).strip() + if line: + s += " %s\n" % line + s += "%s: %s\n>>> " % (category.__name__, message) +@@ -100,58 +112,67 @@ + class PyShellEditorWindow(EditorWindow): + "Regular text edit window in IDLE, supports breakpoints" + ++ rmenu_specs = [("Set Breakpoint", "<>"), ++ ("Clear Breakpoint", "<>")] ++ + def __init__(self, *args): +- self.breakpoints = [] + EditorWindow.__init__(self, *args) +- self.text.bind("<>", self.set_breakpoint_here) +- self.text.bind("<>", self.clear_breakpoint_here) +- self.text.bind("<>", self.flist.open_shell) + + self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(), +- 'breakpoints.lst') ++ 'breakpoints.lst') ++ self.top.bind('<>', self.__configure_new_tab) ++ self.__configure_new_tab() ++ ++ def __configure_new_tab(self, event=None): ++ page = self.text_notebook.last_page().editpage ++ page.breakpoints = [] ++ text = page.text ++ ++ text.bind("<>", ++ utils.callback(self._set_breakpoint_here, text, page)) ++ text.bind("<>", ++ utils.callback(self._clear_breakpoint_here, text, page)) ++ text.bind("<>", self.flist.open_shell) ++ + # whenever a file is changed, restore breakpoints +- if self.io.filename: self.restore_file_breaks() +- def filename_changed_hook(old_hook=self.io.filename_change_hook, ++ if page.io.filename: ++ self.restore_file_breaks(text, page) ++ ++ def filename_changed_hook(old_hook=page.io.filename_change_hook, + self=self): +- self.restore_file_breaks() ++ self.restore_file_breaks(text, page) + old_hook() +- self.io.set_filename_change_hook(filename_changed_hook) ++ page.io.set_filename_change_hook(filename_changed_hook) + +- rmenu_specs = [("Set Breakpoint", "<>"), +- ("Clear Breakpoint", "<>")] +- +- def set_breakpoint(self, lineno): +- text = self.text +- filename = self.io.filename ++ def set_breakpoint(self, lineno, text, page): ++ filename = page.io.filename + text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1)) + try: +- i = self.breakpoints.index(lineno) ++ i = page.breakpoints.index(lineno) + except ValueError: # only add if missing, i.e. do once +- self.breakpoints.append(lineno) ++ page.breakpoints.append(lineno) + try: # update the subprocess debugger + debug = self.flist.pyshell.interp.debugger + debug.set_breakpoint_here(filename, lineno) + except: # but debugger may not be active right now.... + pass + +- def set_breakpoint_here(self, event=None): +- text = self.text +- filename = self.io.filename ++ def _set_breakpoint_here(self, event=None, text=None, page=None): ++ filename = page.io.filename + if not filename: + text.bell() + return + lineno = int(float(text.index("insert"))) +- self.set_breakpoint(lineno) ++ self.set_breakpoint(lineno, text, page) + +- def clear_breakpoint_here(self, event=None): +- text = self.text +- filename = self.io.filename ++ def _clear_breakpoint_here(self, event=None, text=None, page=None): ++ filename = page.io.filename + if not filename: + text.bell() + return + lineno = int(float(text.index("insert"))) + try: +- self.breakpoints.remove(lineno) ++ page.breakpoints.remove(lineno) + except: + pass + text.tag_remove("BREAK", "insert linestart",\ +@@ -163,13 +184,17 @@ + pass + + def clear_file_breaks(self): +- if self.breakpoints: +- text = self.text +- filename = self.io.filename ++ for page in self.text_notebook.pages.itervalues(): ++ page = page.editpage ++ text = page.text ++ filename = page.io.filename ++ ++ if not page.breakpoints: ++ continue + if not filename: + text.bell() +- return +- self.breakpoints = [] ++ continue ++ page.breakpoints = [] + text.tag_remove("BREAK", "1.0", END) + try: + debug = self.flist.pyshell.interp.debugger +@@ -177,7 +202,7 @@ + except: + pass + +- def store_file_breaks(self): ++ def store_file_breaks(self, page): + "Save breakpoints when file is saved" + # XXX 13 Dec 2002 KBK Currently the file must be saved before it can + # be run. The breaks are saved at that time. If we introduce +@@ -200,8 +225,8 @@ + # debugger is loaded) is updated during the save, the visible + # breaks stay synched with the subprocess even if one of these + # unexpected breakpoint deletions occurs. +- breaks = self.breakpoints +- filename = self.io.filename ++ breaks = page.breakpoints ++ filename = page.io.filename + try: + lines = open(self.breakpointPath,"r").readlines() + except IOError: +@@ -210,49 +235,42 @@ + for line in lines: + if not line.startswith(filename + '='): + new_file.write(line) +- self.update_breakpoints() +- breaks = self.breakpoints ++ self.update_breakpoints(page) ++ breaks = page.breakpoints + if breaks: + new_file.write(filename + '=' + str(breaks) + '\n') + new_file.close() + +- def restore_file_breaks(self): +- self.text.update() # this enables setting "BREAK" tags to be visible +- filename = self.io.filename ++ def restore_file_breaks(self, text, page): ++ text.update() # this enables setting "BREAK" tags to be visible ++ filename = page.io.filename + if filename is None: + return + if os.path.isfile(self.breakpointPath): +- lines = open(self.breakpointPath,"r").readlines() ++ lines = open(self.breakpointPath, "r").readlines() + for line in lines: + if line.startswith(filename + '='): + breakpoint_linenumbers = eval(line[len(filename)+1:]) + for breakpoint_linenumber in breakpoint_linenumbers: +- self.set_breakpoint(breakpoint_linenumber) ++ self.set_breakpoint(breakpoint_linenumber, text, page) + +- def update_breakpoints(self): +- "Retrieves all the breakpoints in the current window" +- text = self.text ++ def update_breakpoints(self, page): ++ "Retrieves all the breakpoints in the current page" ++ text = page.text + ranges = text.tag_ranges("BREAK") + linenumber_list = self.ranges_to_linenumbers(ranges) +- self.breakpoints = linenumber_list ++ page.breakpoints = linenumber_list + + def ranges_to_linenumbers(self, ranges): + lines = [] + for index in range(0, len(ranges), 2): +- lineno = int(float(ranges[index])) +- end = int(float(ranges[index+1])) ++ lineno = int(float(str(ranges[index]))) ++ end = int(float(str(ranges[index+1]))) + while lineno < end: + lines.append(lineno) + lineno += 1 + return lines + +-# XXX 13 Dec 2002 KBK Not used currently +-# def saved_change_hook(self): +-# "Extend base method - clear breaks if module is modified" +-# if not self.get_saved(): +-# self.clear_file_breaks() +-# EditorWindow.saved_change_hook(self) +- + def _close(self): + "Extend base method - clear breaks when module is closed" + self.clear_file_breaks() +@@ -589,7 +607,6 @@ + self.save_warnings_filters = warnings.filters[:] + warnings.filterwarnings(action="error", category=SyntaxWarning) + if isinstance(source, types.UnicodeType): +- import IOBinding + try: + source = source.encode(IOBinding.encoding) + except UnicodeError: +@@ -814,30 +831,47 @@ + flist = PyShellFileList(root) + # + OutputWindow.__init__(self, flist, None, None) ++ # remove things related to tabs. The window running the shell is ++ # considered special enough to have a single window for it. ++ ++ # remove tabs area ++ if TTK: ++ self.text_notebook['style'] = 'PyShell.TNotebook' ++ style = Style(self.top) ++ style.layout('PyShell.TNotebook.Tab', [('null', '')]) ++ else: ++ self.text_notebook._tab_set.grid_forget() ++ ++ # remove commands related to tab ++ if 'file' in self.menudict: ++ menu = self.menudict['file'] ++ curr_entry = None ++ i = 0 ++ while True: ++ last_entry, curr_entry = curr_entry, menu.entryconfigure(i) ++ if last_entry == curr_entry: ++ break ++ ++ if 'label' in curr_entry and 'Tab' in curr_entry['label'][-1]: ++ if 'New' in ' '.join(curr_entry['label'][-1]): ++ menu.delete(i) ++ i += 1 ++ self.text.unbind('<>') ++ #self.text.unbind('<>') ++ + # +-## self.config(usetabs=1, indentwidth=8, context_use_ps1=1) + self.usetabs = True + # indentwidth must be 8 when using tabs. See note in EditorWindow: + self.indentwidth = 8 + self.context_use_ps1 = True + # +- text = self.text +- text.configure(wrap="char") +- text.bind("<>", self.enter_callback) +- text.bind("<>", self.linefeed_callback) +- text.bind("<>", self.cancel_callback) +- text.bind("<>", self.eof_callback) +- text.bind("<>", self.open_stack_viewer) +- text.bind("<>", self.toggle_debugger) +- text.bind("<>", self.toggle_jit_stack_viewer) +- if use_subprocess: +- text.bind("<>", self.view_restart_mark) +- text.bind("<>", self.restart_shell) +- # ++ self.wtext = None ++ self.textpage = None ++ self.__configure_new_tab() ++ + self.save_stdout = sys.stdout + self.save_stderr = sys.stderr + self.save_stdin = sys.stdin +- import IOBinding + self.stdout = PseudoFile(self, "stdout", IOBinding.encoding) + self.stderr = PseudoFile(self, "stderr", IOBinding.encoding) + self.console = PseudoFile(self, "console", IOBinding.encoding) +@@ -845,11 +879,37 @@ + sys.stdout = self.stdout + sys.stderr = self.stderr + sys.stdin = self +- # +- self.history = self.History(self.text) +- # ++ + self.pollinterval = 50 # millisec + ++ if TTK: ++ self.set_theme(Style(self.root)) ++ ++ def __configure_new_tab(self): ++ # select the last page (the one added) ++ page = self.text_notebook.last_page().editpage ++ self.wtext = text = page.text ++ self.textpage = page ++ ++ text.configure(wrap="char") ++ text.bind("<>", ++ utils.callback(self._enter_callback, text)) ++ text.bind("<>", ++ utils.callback(self._linefeed_callback, text)) ++ text.bind("<>", ++ utils.callback(self._cancel_callback, text)) ++ text.bind("<>", ++ utils.callback(self._eof_callback, text)) ++ text.bind("<>", self._toggle_debugger) ++ text.bind("<>", self._toggle_jit_stack_viewer) ++ text.bind("<>", self.open_stack_viewer) ++ if use_subprocess: ++ text.bind("<>", ++ utils.callback(self._view_restart_mark, text)) ++ text.bind("<>", self.restart_shell) ++ ++ page.history = self.History(text) ++ + def get_standard_extension_names(self): + return idleConf.GetExtensions(shell_only=True) + +@@ -866,11 +926,11 @@ + def get_warning_stream(self): + return warning_stream + +- def toggle_debugger(self, event=None): ++ def _toggle_debugger(self, event=None): + if self.executing: + tkMessageBox.showerror("Don't debug now", + "You can only toggle the debugger when idle", +- master=self.text) ++ master=self.text_notebook) + self.set_debugger_indicator() + return "break" + else: +@@ -884,7 +944,7 @@ + db = self.interp.getdebugger() + self.setvar("<>", not not db) + +- def toggle_jit_stack_viewer(self, event=None): ++ def _toggle_jit_stack_viewer(self, event=None): + pass # All we need is the variable + + def close_debugger(self): +@@ -930,7 +990,7 @@ + "Kill?", + "The program is still running!\n Do you want to kill it?", + default="ok", +- parent=self.text) ++ parent=self.text_notebook) + if response is False: + return "cancel" + if self.reading: +@@ -938,7 +998,7 @@ + self.canceled = True + self.closing = True + # Wait for poll_subprocess() rescheduling to stop +- self.text.after(2 * self.pollinterval, self.close2) ++ self.text_notebook.after(2 * self.pollinterval, self.close2) + + def close2(self): + return EditorWindow.close(self) +@@ -956,7 +1016,9 @@ + self.interp = None + self.console = None + self.flist.pyshell = None +- self.history = None ++ for page in self.text_notebook.pages.itervalues(): ++ page.editpage.history = None ++ + EditorWindow._close(self) + + def ispythonsource(self, filename): +@@ -996,38 +1058,12 @@ + Tkinter._default_root = None # 03Jan04 KBK What's this? + return True + +- def readline(self): +- save = self.reading +- try: +- self.reading = 1 +- self.top.mainloop() # nested mainloop() +- finally: +- self.reading = save +- line = self.text.get("iomark", "end-1c") +- if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C +- line = "\n" +- if isinstance(line, unicode): +- import IOBinding +- try: +- line = line.encode(IOBinding.encoding) +- except UnicodeError: +- pass +- self.resetoutput() +- if self.canceled: +- self.canceled = 0 +- if not use_subprocess: +- raise KeyboardInterrupt +- if self.endoffile: +- self.endoffile = 0 +- line = "" +- return line +- + def isatty(self): + return True + +- def cancel_callback(self, event=None): ++ def _cancel_callback(self, event=None, text=None): + try: +- if self.text.compare("sel.first", "!=", "sel.last"): ++ if text.compare("sel.first", "!=", "sel.last"): + return # Active selection -- always use default binding + except: + pass +@@ -1047,11 +1083,11 @@ + self.top.quit() # exit the nested mainloop() in readline() + return "break" + +- def eof_callback(self, event): ++ def _eof_callback(self, event, text): + if self.executing and not self.reading: + return # Let the default binding (delete next char) take over +- if not (self.text.compare("iomark", "==", "insert") and +- self.text.compare("insert", "==", "end-1c")): ++ if not (text.compare("iomark", "==", "insert") and ++ text.compare("insert", "==", "end-1c")): + return # Let the default binding (delete next char) take over + if not self.executing: + self.resetoutput() +@@ -1062,24 +1098,24 @@ + self.top.quit() + return "break" + +- def linefeed_callback(self, event): ++ def _linefeed_callback(self, event, text): + # Insert a linefeed without entering anything (still autoindented) + if self.reading: +- self.text.insert("insert", "\n") +- self.text.see("insert") ++ text.insert("insert", "\n") ++ text.see("insert") + else: + self.newline_and_indent_event(event) + return "break" + +- def enter_callback(self, event): ++ def _enter_callback(self, event, text): + if self.executing and not self.reading: + return # Let the default binding (insert '\n') take over + # If some text is selected, recall the selection + # (but only if this before the I/O mark) + try: +- sel = self.text.get("sel.first", "sel.last") ++ sel = text.get("sel.first", "sel.last") + if sel: +- if self.text.compare("sel.last", "<=", "iomark"): ++ if text.compare("sel.last", "<=", "iomark"): + self.recall(sel, event) + return "break" + except: +@@ -1087,51 +1123,52 @@ + # If we're strictly before the line containing iomark, recall + # the current line, less a leading prompt, less leading or + # trailing whitespace +- if self.text.compare("insert", "<", "iomark linestart"): ++ if text.compare("insert", "<", "iomark linestart"): + # Check if there's a relevant stdin range -- if so, use it +- prev = self.text.tag_prevrange("stdin", "insert") +- if prev and self.text.compare("insert", "<", prev[1]): +- self.recall(self.text.get(prev[0], prev[1]), event) ++ prev = text.tag_prevrange("stdin", "insert") ++ if prev and text.compare("insert", "<", prev[1]): ++ self.recall(text.get(prev[0], prev[1]), event) + return "break" +- next = self.text.tag_nextrange("stdin", "insert") +- if next and self.text.compare("insert lineend", ">=", next[0]): +- self.recall(self.text.get(next[0], next[1]), event) ++ next = text.tag_nextrange("stdin", "insert") ++ if next and text.compare("insert lineend", ">=", next[0]): ++ self.recall(text.get(next[0], next[1]), event) + return "break" + # No stdin mark -- just get the current line, less any prompt +- indices = self.text.tag_nextrange("console", "insert linestart") ++ indices = text.tag_nextrange("console", "insert linestart") + if indices and \ +- self.text.compare(indices[0], "<=", "insert linestart"): +- self.recall(self.text.get(indices[1], "insert lineend"), event) ++ text.compare(indices[0], "<=", "insert linestart"): ++ self.recall(text.get(indices[1], "insert lineend"), event) + else: +- self.recall(self.text.get("insert linestart", "insert lineend"), event) ++ self.recall(text.get("insert linestart", "insert lineend"), ++ event) + return "break" + # If we're between the beginning of the line and the iomark, i.e. + # in the prompt area, move to the end of the prompt +- if self.text.compare("insert", "<", "iomark"): +- self.text.mark_set("insert", "iomark") ++ if text.compare("insert", "<", "iomark"): ++ text.mark_set("insert", "iomark") + # If we're in the current input and there's only whitespace + # beyond the cursor, erase that whitespace first +- s = self.text.get("insert", "end-1c") ++ s = text.get("insert", "end-1c") + if s and not s.strip(): +- self.text.delete("insert", "end-1c") ++ text.delete("insert", "end-1c") + # If we're in the current input before its last line, + # insert a newline right at the insert point +- if self.text.compare("insert", "<", "end-1c linestart"): ++ if text.compare("insert", "<", "end-1c linestart"): + self.newline_and_indent_event(event) + return "break" + # We're in the last line; append a newline and submit it +- self.text.mark_set("insert", "end-1c") ++ text.mark_set("insert", "end-1c") + if self.reading: +- self.text.insert("insert", "\n") +- self.text.see("insert") ++ text.insert("insert", "\n") ++ text.see("insert") + else: + self.newline_and_indent_event(event) +- self.text.tag_add("stdin", "iomark", "end-1c") +- self.text.update_idletasks() ++ text.tag_add("stdin", "iomark", "end-1c") ++ text.update_idletasks() + if self.reading: + self.top.quit() # Break out of recursive mainloop() in raw_input() + else: +- self.runit() ++ self._runit(text) + return "break" + + def recall(self, s, event): +@@ -1139,15 +1176,15 @@ + s = re.sub(r'^\s*\n', '' , s) + s = re.sub(r'\n\s*$', '', s) + lines = s.split('\n') +- self.text.undo_block_start() ++ self.wtext.undo_block_start() + try: +- self.text.tag_remove("sel", "1.0", "end") +- self.text.mark_set("insert", "end-1c") +- prefix = self.text.get("insert linestart", "insert") ++ self.wtext.tag_remove("sel", "1.0", "end") ++ self.wtext.mark_set("insert", "end-1c") ++ prefix = self.wtext.get("insert linestart", "insert") + if prefix.rstrip().endswith(':'): + self.newline_and_indent_event(event) +- prefix = self.text.get("insert linestart", "insert") +- self.text.insert("insert", lines[0].strip()) ++ prefix = self.wtext.get("insert linestart", "insert") ++ self.wtext.insert("insert", lines[0].strip()) + if len(lines) > 1: + orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0) + new_base_indent = re.search(r'^([ \t]*)', prefix).group(0) +@@ -1155,13 +1192,13 @@ + if line.startswith(orig_base_indent): + # replace orig base indentation with new indentation + line = new_base_indent + line[len(orig_base_indent):] +- self.text.insert('insert', '\n'+line.rstrip()) ++ self.wtext.insert('insert', '\n'+line.rstrip()) + finally: +- self.text.see("insert") +- self.text.undo_block_stop() ++ self.wtext.see("insert") ++ self.wtext.undo_block_stop() + +- def runit(self): +- line = self.text.get("iomark", "end-1c") ++ def _runit(self, text): ++ line = text.get("iomark", "end-1c") + # Strip off last newline and surrounding whitespace. + # (To allow you to hit return twice to end a statement.) + i = len(line) +@@ -1179,18 +1216,18 @@ + return self.interp.remote_stack_viewer() + try: + sys.last_traceback +- except: ++ except AttributeError: + tkMessageBox.showerror("No stack trace", + "There is no stack trace yet.\n" + "(sys.last_traceback is not defined)", +- master=self.text) ++ master=self.text_notebook) + return + from StackViewer import StackBrowser + sv = StackBrowser(self.root, self.flist) + +- def view_restart_mark(self, event=None): +- self.text.see("iomark") +- self.text.see("restart") ++ def _view_restart_mark(self, event=None, text=None): ++ text.see("iomark") ++ text.see("restart") + + def restart_shell(self, event=None): + self.interp.restart_subprocess() +@@ -1202,25 +1239,30 @@ + except: + s = "" + self.console.write(s) +- self.text.mark_set("insert", "end-1c") ++ ++ curr_page = self.current_page ++ if not curr_page: ++ return ++ ++ curr_page.text.mark_set("insert", "end-1c") + self.set_line_and_column() +- self.io.reset_undo() ++ curr_page.io.reset_undo() + + def resetoutput(self): +- source = self.text.get("iomark", "end-1c") +- if self.history: +- self.history.history_store(source) +- if self.text.get("end-2c") != "\n": +- self.text.insert("end-1c", "\n") +- self.text.mark_set("iomark", "end-1c") ++ source = self.wtext.get("iomark", "end-1c") ++ if self.textpage.history: ++ self.textpage.history.history_store(source) ++ if self.wtext.get("end-2c") != "\n": ++ self.wtext.insert("end-1c", "\n") ++ self.wtext.mark_set("iomark", "end-1c") + self.set_line_and_column() + sys.stdout.softspace = 0 + + def write(self, s, tags=()): + try: +- self.text.mark_gravity("iomark", "right") +- OutputWindow.write(self, s, tags, "iomark") +- self.text.mark_gravity("iomark", "left") ++ self.wtext.mark_gravity("iomark", "right") ++ OutputWindow.write(self, s, tags, "iomark", self.wtext) ++ self.wtext.mark_gravity("iomark", "left") + except: + pass + if self.canceled: +@@ -1381,6 +1423,19 @@ + # start editor and/or shell windows: + root = Tk(className="Idle") + ++ if TTK: ++ # create base styles used along idle files ++ style = Style() ++ ++ rootbg = style.map('.', 'background') ++ fstyle = {'background': []} ++ for sspec in rootbg: ++ if 'active' in sspec[:-1]: ++ fstyle['background'].append(('!disabled', sspec[-1])) ++ break ++ style.map('RootColor.TFrame', **fstyle) ++ # end styles ++ + fixwordbreaks(root) + root.withdraw() + flist = PyShellFileList(root) +Index: ParenMatch.py +=================================================================== +--- ParenMatch.py (revision 63995) ++++ ParenMatch.py (revision 65541) +@@ -56,14 +56,13 @@ + RESTORE_SEQUENCES = ("", "", + "", "") + +- def __init__(self, editwin): +- self.editwin = editwin +- self.text = editwin.text ++ def __init__(self, editpage): ++ self.editpage = editpage ++ self.text = editpage.text + # Bind the check-restore event to the function restore_event, + # so that we can then use activate_restore (which calls event_add) + # and deactivate_restore (which calls event_delete). +- editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, +- self.restore_event) ++ editpage.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event) + self.counter = 0 + self.is_restore_active = 0 + self.set_style(self.STYLE) +@@ -90,7 +89,7 @@ + self.set_timeout = self.set_timeout_none + + def flash_paren_event(self, event): +- indices = HyperParser(self.editwin, "insert").get_surrounding_brackets() ++ indices = HyperParser(self.editpage, "insert").get_surrounding_brackets() + if indices is None: + self.warn_mismatched() + return +@@ -103,7 +102,7 @@ + closer = self.text.get("insert-1c") + if closer not in _openers: + return +- hp = HyperParser(self.editwin, "insert-1c") ++ hp = HyperParser(self.editpage, "insert-1c") + if not hp.is_in_code(): + return + indices = hp.get_surrounding_brackets(_openers[closer], True) +@@ -159,14 +158,13 @@ + if index != self.text.index("insert"): + self.handle_restore_timer(c) + else: +- self.editwin.text_frame.after(CHECK_DELAY, callme, callme) +- self.editwin.text_frame.after(CHECK_DELAY, callme, callme) ++ self.text.master.after(CHECK_DELAY, callme, callme) ++ self.text.master.after(CHECK_DELAY, callme, callme) + + def set_timeout_last(self): + """The last highlight created will be removed after .5 sec""" + # associate a counter with an event; only disable the "paren" + # tag if the event is for the most recent timer. + self.counter += 1 +- self.editwin.text_frame.after(self.FLASH_DELAY, +- lambda self=self, c=self.counter: \ +- self.handle_restore_timer(c)) ++ self.text.master.after(self.FLASH_DELAY, ++ lambda self=self, c=self.counter: self.handle_restore_timer(c)) +Index: config-keys.def +=================================================================== +--- config-keys.def (revision 63995) ++++ config-keys.def (revision 65541) +@@ -8,6 +8,10 @@ + # configuration gui. + + [IDLE Classic Windows] ++close-tab= ++new-tab= ++next-tab= ++prev-tab= + copy= + cut= + paste= +@@ -59,6 +63,10 @@ + del-word-right= + + [IDLE Classic Unix] ++close-tab= ++new-tab= ++next-tab= ++prev-tab= + copy= + cut= + paste= +@@ -110,6 +118,10 @@ + del-word-right= + + [IDLE Classic Mac] ++close-tab= ++new-tab= ++next-tab= ++prev-tab= + copy= + cut= + paste= +@@ -161,6 +173,10 @@ + del-word-right= + + [IDLE Classic OSX] ++close-tab= ++new-tab = ++next-tab= ++prev-tab= + toggle-tabs = + interrupt-execution = + untabify-region = +Index: Debugger.py +=================================================================== +--- Debugger.py (revision 63995) ++++ Debugger.py (revision 65541) +@@ -1,11 +1,17 @@ + import os + import bdb + import types +-from Tkinter import * ++from Tkinter import Frame, Button, Entry, Checkbutton, Label, Scrollbar, \ ++ Canvas, BooleanVar ++from Tkconstants import W, LEFT, DISABLED, X, Y, BOTH, NW, GROOVE, RIGHT ++ ++import macosxSupport + from WindowList import ListedToplevel + from ScrolledList import ScrolledList +-import macosxSupport ++from configHandler import idleConf + ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Frame, Button, Checkbutton, Scrollbar + + class Idb(bdb.Bdb): + +@@ -92,7 +98,7 @@ + self.top.bind("", self.close) + # + self.bframe = bframe = Frame(top) +- self.bframe.pack(anchor="w") ++ self.bframe.pack(anchor=W) + self.buttons = bl = [] + # + self.bcont = b = Button(bframe, text="Go", command=self.cont) +@@ -107,11 +113,11 @@ + bl.append(b) + # + for b in bl: +- b.configure(state="disabled") +- b.pack(side="left") ++ b.configure(state=DISABLED) ++ b.pack(side=LEFT) + # + self.cframe = cframe = Frame(bframe) +- self.cframe.pack(side="left") ++ self.cframe.pack(side=LEFT) + # + if not self.vstack: + self.__class__.vstack = BooleanVar(top) +@@ -136,18 +142,18 @@ + text="Globals", command=self.show_globals, variable=self.vglobals) + self.bglobals.grid(row=1, column=1) + # +- self.status = Label(top, anchor="w") +- self.status.pack(anchor="w") +- self.error = Label(top, anchor="w") +- self.error.pack(anchor="w", fill="x") ++ self.status = Label(top, anchor=W) ++ self.status.pack(anchor=W) ++ self.error = Label(top, anchor=W) ++ self.error.pack(anchor=W, fill=X) + self.errorbg = self.error.cget("background") + # + self.fstack = Frame(top, height=1) +- self.fstack.pack(expand=1, fill="both") ++ self.fstack.pack(expand=1, fill=BOTH) + self.flocals = Frame(top) +- self.flocals.pack(expand=1, fill="both") ++ self.flocals.pack(expand=1, fill=BOTH) + self.fglobals = Frame(top, height=1) +- self.fglobals.pack(expand=1, fill="both") ++ self.fglobals.pack(expand=1, fill=BOTH) + # + if self.vstack.get(): + self.show_stack() +@@ -155,6 +161,7 @@ + self.show_locals() + if self.vglobals.get(): + self.show_globals() ++ # + + def interaction(self, message, frame, info=None): + self.frame = frame +@@ -313,12 +320,14 @@ + "Load PyShellEditorWindow breakpoints into subprocess debugger" + pyshell_edit_windows = self.pyshell.flist.inversedict.keys() + for editwin in pyshell_edit_windows: +- filename = editwin.io.filename +- try: +- for lineno in editwin.breakpoints: +- self.set_breakpoint_here(filename, lineno) +- except AttributeError: +- continue ++ for page in editwin.text_notebook.pages.itervalues(): ++ editpage = page.editpage ++ filename = editpage.io.filename ++ try: ++ for lineno in editpage.breakpoints: ++ self.set_breakpoint_here(filename, lineno) ++ except AttributeError: ++ continue + + class StackViewer(ScrolledList): + +@@ -348,8 +357,7 @@ + funcname = code.co_name + import linecache + sourceline = linecache.getline(filename, lineno) +- import string +- sourceline = string.strip(sourceline) ++ sourceline = sourceline.strip() + if funcname in ("?", "", None): + item = "%s, line %d: %s" % (modname, lineno, sourceline) + else: +@@ -413,24 +421,25 @@ + height = 20*len(dict) # XXX 20 == observed height of Entry widget + self.master = master + self.title = title ++ + import repr + self.repr = repr.Repr() + self.repr.maxstring = 60 + self.repr.maxother = 60 + self.frame = frame = Frame(master) +- self.frame.pack(expand=1, fill="both") +- self.label = Label(frame, text=title, borderwidth=2, relief="groove") +- self.label.pack(fill="x") +- self.vbar = vbar = Scrollbar(frame, name="vbar") +- vbar.pack(side="right", fill="y") ++ self.frame.pack(expand=1, fill=BOTH) ++ self.label = Label(frame, text=title, borderwidth=2, relief=GROOVE) ++ self.label.pack(fill=X) ++ vbar = Scrollbar(frame, name="vbar") ++ vbar.pack(side=RIGHT, fill=Y) + self.canvas = canvas = Canvas(frame, + height=min(300, max(40, height)), + scrollregion=(0, 0, width, height)) +- canvas.pack(side="left", fill="both", expand=1) ++ canvas.pack(side=LEFT, fill=BOTH, expand=1) + vbar["command"] = canvas.yview + canvas["yscrollcommand"] = vbar.set + self.subframe = subframe = Frame(canvas) +- self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw") ++ self.sfid = canvas.create_window(0, 0, window=subframe, anchor=NW) + self.load_dict(dict) + + dict = -1 +@@ -458,10 +467,10 @@ + if rpc_client: + svalue = svalue[1:-1] + l = Label(subframe, text=name) +- l.grid(row=row, column=0, sticky="nw") ++ l.grid(row=row, column=0, sticky=NW) + l = Entry(subframe, width=0, borderwidth=0) + l.insert(0, svalue) +- l.grid(row=row, column=1, sticky="nw") ++ l.grid(row=row, column=1, sticky=NW) + row = row+1 + self.dict = dict + # XXX Could we use a callback for the following? +Index: configDialog.py +=================================================================== +--- configDialog.py (revision 63995) ++++ configDialog.py (revision 65541) +@@ -7,19 +7,30 @@ + + Note that tab width in IDLE is currently fixed at eight due to Tk issues. + Refer to comments in EditorWindow autoindent code for details. +- + """ +-from Tkinter import * ++from Tkinter import Toplevel, Frame, Button, Scale, Label, LabelFrame, Text, \ ++ Listbox, Scrollbar, Checkbutton, Radiobutton, Entry, \ ++ Checkbutton, StringVar, BooleanVar, IntVar ++from Tkconstants import LEFT, RIGHT, BOTTOM, TOP, BOTH, GROOVE, SOLID, NONE, \ ++ END, DISABLED, NSEW, Y, X, W, E, HORIZONTAL, NS, EW, \ ++ N, ANCHOR, NORMAL + import tkMessageBox, tkColorChooser, tkFont +-import string + ++from stylist import PoorManStyle ++from tabbedpages import get_tabbedpage + from configHandler import idleConf ++from keybindingDialog import GetKeysDialog + from dynOptionMenuWidget import DynOptionMenu +-from tabbedpages import TabbedPageSet +-from keybindingDialog import GetKeysDialog ++from configHelpSourceEdit import GetHelpSourceDialog + from configSectionNameDialog import GetCfgSectionNameDialog +-from configHelpSourceEdit import GetHelpSourceDialog + ++TabbedPageSet = get_tabbedpage() ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Frame, Button, Checkbutton, LabelFrame, LabeledScale, \ ++ Combobox, Checkbutton, Entry, Radiobutton, Scrollbar, \ ++ Label, Style ++ + class ConfigDialog(Toplevel): + + def __init__(self,parent,title): +@@ -27,8 +38,8 @@ + self.wm_withdraw() + + self.configure(borderwidth=5) +- self.geometry("+%d+%d" % (parent.winfo_rootx()+20, +- parent.winfo_rooty()+30)) ++ self.geometry("+%d+%d" % (parent.winfo_rootx() + 20, ++ parent.winfo_rooty() + 30)) + #Theme Elements. Each theme element key is its display name. + #The first value of the tuple is the sample area tag name. + #The second value is the display name list sort index. +@@ -47,8 +58,9 @@ + 'Shell Stderr Text':('stderr','12'), + } + self.ResetChangedItems() #load initial values in changed items dict ++ self.SetupStyles() + self.CreateWidgets() +- self.resizable(height=FALSE,width=FALSE) ++ self.resizable(height=False, width=False) + self.transient(parent) + self.grab_set() + self.protocol("WM_DELETE_WINDOW", self.Cancel) +@@ -64,34 +76,54 @@ + self.wm_deiconify() + self.wait_window() + ++ def SetupStyles(self): ++ if TTK: ++ style = Style(self.master) ++ style.configure('S.TButton', padding=[6, 3]) ++ style.configure('S2.TFrame', padding=2) ++ style.configure('Color.TFrame', background='blue') ++ self.ttkstyle = style ++ self.style = lambda w, style: w.configure(style=style) ++ else: ++ self.ttkstyle = PoorManStyle(self, styles={ ++ 'S.TButton': {'pady': 6, 'padx': 3}, ++ 'S2.TFrame': {'padx': 2, 'pady': 2} ++ }, cfgstyles={'Color.TFrame': 'frameColourSet'}) ++ self.style = self.ttkstyle.style_it ++ + def CreateWidgets(self): + self.tabPages = TabbedPageSet(self, +- page_names=['Fonts/Tabs','Highlighting','Keys','General']) +- frameActionButtons = Frame(self,pady=2) ++ page_names=['Fonts/Tabs','Highlighting','Keys','General']) ++ frameActionButtons = Frame(self) + #action buttons + self.buttonHelp = Button(frameActionButtons,text='Help', +- command=self.Help,takefocus=FALSE, +- padx=6,pady=3) +- self.buttonOk = Button(frameActionButtons,text='Ok', +- command=self.Ok,takefocus=FALSE, +- padx=6,pady=3) +- self.buttonApply = Button(frameActionButtons,text='Apply', +- command=self.Apply,takefocus=FALSE, +- padx=6,pady=3) +- self.buttonCancel = Button(frameActionButtons,text='Cancel', +- command=self.Cancel,takefocus=FALSE, +- padx=6,pady=3) ++ command=self.Help, takefocus=False) ++ self.buttonOk = Button(frameActionButtons, text='Ok', ++ command=self.Ok, takefocus=False) ++ self.buttonApply = Button(frameActionButtons, text='Apply', ++ command=self.Apply, takefocus=False) ++ self.buttonCancel = Button(frameActionButtons, text='Cancel', ++ command=self.Cancel, takefocus=False) ++ ++ # Apply styles ++ s = self.style ++ s(frameActionButtons, 'RootColor.TFrame') ++ s(self.buttonHelp, 'S.TButton') ++ s(self.buttonOk, 'S.TButton') ++ s(self.buttonApply, 'S.TButton') ++ s(self.buttonCancel, 'S.TButton') ++ + self.CreatePageFontTab() + self.CreatePageHighlight() + self.CreatePageKeys() + self.CreatePageGeneral() +- 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) ++ self.buttonHelp.pack(side=LEFT, pady=6) ++ self.buttonApply.pack(side=RIGHT, pady=6) ++ self.buttonOk.pack(side=RIGHT, padx=6, pady=6) ++ self.buttonCancel.pack(side=RIGHT, pady=6) ++ frameActionButtons.pack(side=BOTTOM, fill=X, expand=True) + Frame(self, height=2, borderwidth=0).pack(side=BOTTOM) +- self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH) ++ self.tabPages.pack(side=TOP,expand=True,fill=BOTH) + + def CreatePageFontTab(self): + #tkVars +@@ -113,8 +145,8 @@ + frameFontParam=Frame(frameFont) + labelFontNameTitle=Label(frameFontName,justify=LEFT, + text='Font Face :') +- self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE, +- exportselection=FALSE) ++ self.listFontName=Listbox(frameFontName,height=5,takefocus=False, ++ exportselection=False) + self.listFontName.bind('',self.OnListFontButtonRelease) + scrollFont=Scrollbar(frameFontName) + scrollFont.config(command=self.listFontName.yview) +@@ -127,122 +159,143 @@ + frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1) + self.labelFontSample=Label(frameFontSample, + text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]', +- justify=LEFT,font=self.editFont) ++ justify=LEFT, font=self.editFont) + #frameIndent + frameIndentSize=Frame(frameIndent) + labelSpaceNumTitle=Label(frameIndentSize, justify=LEFT, + text='Python Standard: 4 Spaces!') +- self.scaleSpaceNum=Scale(frameIndentSize, variable=self.spaceNum, +- orient='horizontal', +- tickinterval=2, from_=2, to=16) + #widget packing + #body +- frameFont.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH) ++ frameFont.pack(side=LEFT,padx=5,pady=5,expand=True,fill=BOTH) + frameIndent.pack(side=LEFT,padx=5,pady=5,fill=Y) + #frameFont + 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) +- self.listFontName.pack(side=LEFT,expand=TRUE,fill=X) ++ self.listFontName.pack(side=LEFT,expand=True,fill=X) + scrollFont.pack(side=LEFT,fill=Y) + labelFontSizeTitle.pack(side=LEFT,anchor=W) + self.optMenuFontSize.pack(side=LEFT,anchor=W) + checkFontBold.pack(side=LEFT,anchor=W,padx=20) +- frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH) +- self.labelFontSample.pack(expand=TRUE,fill=BOTH) ++ frameFontSample.pack(side=TOP,padx=5,pady=5,expand=True,fill=BOTH) ++ self.labelFontSample.pack(expand=1, fill=Y) + #frameIndent + frameIndentSize.pack(side=TOP,fill=X) +- labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5) +- self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X) ++ labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5, pady=6) ++ ++ if TTK: ++ self.scaleSpaceNum = LabeledScale(frameIndentSize, self.spaceNum, ++ from_=2, to=16, padding=2) ++ else: ++ self.scaleSpaceNum=Scale(frameIndentSize, variable=self.spaceNum, ++ orient='horizontal', from_=2, to=16, tickinterval=2) ++ ++ self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X) ++ + return frame + + def CreatePageHighlight(self): +- self.builtinTheme=StringVar(self) +- self.customTheme=StringVar(self) +- self.fgHilite=BooleanVar(self) +- self.colour=StringVar(self) +- self.fontName=StringVar(self) +- self.themeIsBuiltin=BooleanVar(self) +- self.highlightTarget=StringVar(self) ++ self.builtinTheme = StringVar(self) ++ self.customTheme = StringVar(self) ++ self.fgHilite = BooleanVar(self) ++ self.colour = StringVar(self) ++ self.fontName = StringVar(self) ++ self.themeIsBuiltin = BooleanVar(self) ++ self.highlightTarget = StringVar(self) ++ #self.themeFontBold = BooleanVar(self) + ##widget creation + #body frame +- frame=self.tabPages.pages['Highlighting'].frame ++ frame = self.tabPages.pages['Highlighting'].frame + #body section frames +- frameCustom=LabelFrame(frame,borderwidth=2,relief=GROOVE, +- text=' Custom Highlighting ') +- frameTheme=LabelFrame(frame,borderwidth=2,relief=GROOVE, +- text=' Highlighting Theme ') ++ 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, +- takefocus=FALSE,highlightthickness=0,wrap=NONE) +- text=self.textHighlightSample +- text.bind('',lambda e: 'break') +- text.bind('',lambda e: 'break') +- textAndTags=(('#you can click here','comment'),('\n','normal'), +- ('#to choose items','comment'),('\n','normal'),('def','keyword'), +- (' ','normal'),('func','definition'),('(param):','normal'), +- ('\n ','normal'),('"""string"""','string'),('\n var0 = ','normal'), +- ("'string'",'string'),('\n var1 = ','normal'),("'selected'",'hilite'), +- ('\n var2 = ','normal'),("'found'",'hit'), +- ('\n var3 = ','normal'),('list', 'builtin'), ('(','normal'), +- ('None', 'builtin'),(')\n\n','normal'), +- (' error ','error'),(' ','normal'),('cursor |','cursor'), +- ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'), +- (' ','normal'),('stderr','stderr'),('\n','normal')) ++ self.textHighlightSample = Text(frameCustom, relief=SOLID, ++ borderwidth=1, font=('courier', 12, ''), cursor='hand2', width=21, ++ height=11, takefocus=False, highlightthickness=0, wrap=NONE) ++ text = self.textHighlightSample ++ text.bind('', lambda e: 'break') ++ text.bind('', lambda e: 'break') ++ textAndTags = ( ++ ('#you can click here','comment'), ('\n','normal'), ++ ('#to choose items', 'comment'), ('\n', 'normal'), ++ ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'), ++ ('(param):', 'normal'), ('\n ', 'normal'), ++ ('"""string"""', 'string'), ++ ('\n var0 = ','normal'), ("'string'", 'string'), ++ ('\n var1 = ', 'normal'), ("'selected'", 'hilite'), ++ ('\n var2 = ', 'normal'), ("'found'", 'hit'), ++ ('\n var3 = ', 'normal'), ('list', 'builtin'), ('(', 'normal'), ++ ('None', 'builtin'), (')\n\n', 'normal'), ++ (' error ', 'error'), (' ', 'normal'), ('cursor |', 'cursor'), ++ ('\n ', 'normal'), ('shell', 'console'), (' ', 'normal'), ++ ('stdout', 'stdout'), (' ', 'normal'), ('stderr', 'stderr'), ++ ('\n', 'normal') ++ ) + for txTa in textAndTags: +- text.insert(END,txTa[0],txTa[1]) ++ text.insert(END, txTa[0], txTa[1]) + for element in self.themeElements.keys(): +- text.tag_bind(self.themeElements[element][0],'', +- lambda event,elem=element: event.widget.winfo_toplevel() +- .highlightTarget.set(elem)) ++ text.tag_bind(self.themeElements[element][0], '', ++ lambda event, elem=element: ++ event.widget.winfo_toplevel().highlightTarget.set(elem)) + text.config(state=DISABLED) +- self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1) +- frameFgBg=Frame(frameCustom) +- buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :', +- command=self.GetColour,highlightthickness=0) +- self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet, +- self.highlightTarget,None,highlightthickness=0)#,command=self.SetHighlightTargetBinding +- self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite, +- value=1,text='Foreground',command=self.SetColourSampleBinding) +- self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite, +- value=0,text='Background',command=self.SetColourSampleBinding) ++ ++ self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1) ++ self.style(self.frameColourSet, 'Color.TFrame') ++ ++ frameFgBg = Frame(frameCustom) ++ buttonSetColour = Button(self.frameColourSet, ++ text='Choose Colour for :', command=self.GetColour) ++ self.optMenuHighlightTarget = DynOptionMenu(self.frameColourSet, ++ self.highlightTarget, None) ++ #self.optBoldText = Checkbutton(self.frameColourSet, text="Bold", ++ # variable=self.themeFontBold) ++ self.radioFg = Radiobutton(frameFgBg, variable=self.fgHilite, ++ value=1, text='Foreground', command=self.SetColourSampleBinding) ++ self.radioBg = Radiobutton(frameFgBg, variable=self.fgHilite, ++ value=0, text='Background', command=self.SetColourSampleBinding) + self.fgHilite.set(1) +- buttonSaveCustomTheme=Button(frameCustom, +- text='Save as New Custom Theme',command=self.SaveAsNewTheme) ++ buttonSaveCustomTheme = Button(frameCustom, ++ text='Save as New Custom Theme', command=self.SaveAsNewTheme) + #frameTheme +- labelTypeTitle=Label(frameTheme,text='Select : ') +- self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin, +- value=1,command=self.SetThemeType,text='a Built-in Theme') +- self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin, +- value=0,command=self.SetThemeType,text='a Custom Theme') +- self.optMenuThemeBuiltin=DynOptionMenu(frameTheme, +- self.builtinTheme,None,command=None) +- self.optMenuThemeCustom=DynOptionMenu(frameTheme, +- self.customTheme,None,command=None) +- self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme', +- command=self.DeleteCustomTheme) ++ labelTypeTitle = Label(frameTheme, text='Select : ') ++ self.radioThemeBuiltin = Radiobutton(frameTheme, ++ variable=self.themeIsBuiltin, value=1, ++ command=self.SetThemeType, text='a Built-in Theme') ++ self.radioThemeCustom = Radiobutton(frameTheme, ++ variable=self.themeIsBuiltin, value=0, ++ command=self.SetThemeType, text='a Custom Theme') ++ self.optMenuThemeBuiltin = DynOptionMenu(frameTheme, ++ self.builtinTheme, None, command=None) ++ self.optMenuThemeCustom = DynOptionMenu(frameTheme, ++ self.customTheme, None, command=None) ++ self.buttonDeleteCustomTheme = Button(frameTheme, ++ text='Delete Custom Theme', command=self.DeleteCustomTheme) + ##widget packing + #body +- frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH) +- frameTheme.pack(side=LEFT,padx=5,pady=5,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 +- 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, ++ 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, + fill=BOTH) +- buttonSetColour.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=4) +- self.optMenuHighlightTarget.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=3) +- self.radioFg.pack(side=LEFT,anchor=E) +- self.radioBg.pack(side=RIGHT,anchor=W) +- buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5) ++ buttonSetColour.pack(side=TOP, expand=True, fill=X, padx=8, pady=4) ++ self.optMenuHighlightTarget.pack(side=LEFT, anchor=N, expand=True, ++ fill=X, padx=8, pady=3) ++ #self.optBoldText.pack(side=RIGHT, anchor=N, padx=8, pady=3) ++ self.radioFg.pack(side=LEFT, anchor=E) ++ self.radioBg.pack(side=RIGHT, anchor=W) ++ buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5) + #frameTheme +- 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) +- self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5) +- self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5) +- self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,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) ++ self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5) ++ self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5) ++ self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5) + return frame + + def CreatePageKeys(self): +@@ -265,8 +318,8 @@ + labelTargetTitle=Label(frameTarget,text='Action - Key(s)') + scrollTargetY=Scrollbar(frameTarget) + scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL) +- self.listBindings=Listbox(frameTarget,takefocus=FALSE, +- exportselection=FALSE) ++ self.listBindings=Listbox(frameTarget,takefocus=False, ++ exportselection=False) + self.listBindings.bind('',self.KeyBindingSelected) + scrollTargetY.config(command=self.listBindings.yview) + scrollTargetX.config(command=self.listBindings.xview) +@@ -275,8 +328,11 @@ + self.buttonNewKeys=Button(frameCustom,text='Get New Keys for Selection', + command=self.GetNewKeys,state=DISABLED) + #frameKeySets +- frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0) +- for i in range(2)] ++ frames = [] ++ for i in range(2): ++ f = Frame(frameKeySets, borderwidth=0) ++ self.style(f, 'S2.TFrame') ++ frames.append(f) + self.radioKeysBuiltin=Radiobutton(frames[0],variable=self.keysAreBuiltin, + value=1,command=self.SetKeysType,text='Use a Built-in Key Set') + self.radioKeysCustom=Radiobutton(frames[0],variable=self.keysAreBuiltin, +@@ -291,11 +347,11 @@ + text='Save as New Custom Key Set',command=self.SaveAsNewKeySet) + ##widget packing + #body +- frameCustom.pack(side=BOTTOM,padx=5,pady=5,expand=TRUE,fill=BOTH) ++ frameCustom.pack(side=BOTTOM,padx=5,pady=5,expand=True,fill=BOTH) + frameKeySets.pack(side=BOTTOM,padx=5,pady=5,fill=BOTH) + #frameCustom + self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5) +- frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH) ++ frameTarget.pack(side=LEFT,padx=5,pady=5,expand=True,fill=BOTH) + #frame target + frameTarget.columnconfigure(0,weight=1) + frameTarget.rowconfigure(1,weight=1) +@@ -316,14 +372,16 @@ + + def CreatePageGeneral(self): + #tkVars +- self.winWidth=StringVar(self) +- self.winHeight=StringVar(self) +- self.paraWidth=StringVar(self) +- self.startupEdit=IntVar(self) +- self.autoSave=IntVar(self) +- self.encoding=StringVar(self) +- self.userHelpBrowser=BooleanVar(self) +- self.helpBrowser=StringVar(self) ++ self.winWidth = StringVar(self) ++ self.winHeight = StringVar(self) ++ self.paraWidth = StringVar(self) ++ self.startupEdit = IntVar(self) ++ self.autoSave = IntVar(self) ++ self.encoding = StringVar(self) ++ self.themename = StringVar(self) ++ self.fileintab = BooleanVar(self) ++ self.userHelpBrowser = BooleanVar(self) ++ self.helpBrowser = StringVar(self) + #widget creation + #body + frame=self.tabPages.pages['General'].frame +@@ -335,6 +393,9 @@ + frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE) + frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE) + frameEncoding=Frame(frame,borderwidth=2,relief=GROOVE) ++ frameModTab = Frame(frame, borderwidth=2, relief=GROOVE) ++ if TTK: ++ frameTheme = Frame(frame, borderwidth=2, relief=GROOVE) + frameHelp=LabelFrame(frame,borderwidth=2,relief=GROOVE, + text=' Additional Help Sources ') + #frameRun +@@ -371,12 +432,21 @@ + value="utf-8",text="UTF-8") + radioEncNone=Radiobutton(frameEncoding,variable=self.encoding, + value="none",text="None") ++ # frameModTab ++ labelMTab = Label(frameModTab, text="Open files and modules in tabs") ++ checkMTab = Checkbutton(frameModTab, variable=self.fileintab, ++ text="Yes") ++ #frameTheme ++ if TTK: ++ labelTheme = Label(frameTheme, text="Display Theme") ++ comboTheme = Combobox(frameTheme, textvariable=self.themename, ++ values=self.ttkstyle.theme_names(), state='readonly') + #frameHelp + frameHelpList=Frame(frameHelp) + frameHelpListButtons=Frame(frameHelpList) + scrollHelpList=Scrollbar(frameHelpList) +- self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE, +- exportselection=FALSE) ++ self.listHelp=Listbox(frameHelpList, height=4, takefocus=False, ++ exportselection=False) + scrollHelpList.config(command=self.listHelp.yview) + self.listHelp.config(yscrollcommand=scrollHelpList.set) + self.listHelp.bind('',self.HelpSourceSelected) +@@ -393,7 +463,10 @@ + frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X) + frameParaSize.pack(side=TOP,padx=5,pady=5,fill=X) + frameEncoding.pack(side=TOP,padx=5,pady=5,fill=X) +- frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH) ++ frameModTab.pack(side=TOP, padx=5, pady=5, fill=X) ++ if TTK: ++ frameTheme.pack(side=TOP, padx=5, pady=5, fill=X) ++ frameHelp.pack(side=TOP,padx=5,pady=5,expand=True,fill=BOTH) + #frameRun + labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) + radioStartupShell.pack(side=RIGHT,anchor=W,padx=5,pady=5) +@@ -416,14 +489,22 @@ + radioEncNone.pack(side=RIGHT,anchor=E,pady=5) + radioEncUTF8.pack(side=RIGHT,anchor=E,pady=5) + radioEncLocale.pack(side=RIGHT,anchor=E,pady=5) ++ #frameModTab ++ labelMTab.pack(side=LEFT, anchor=W, padx=5, pady=5) ++ checkMTab.pack(side=RIGHT, anchor=E, padx=5, pady=5) ++ #frameTheme ++ if TTK: ++ labelTheme.pack(side=LEFT, anchor=W, padx=5, pady=5) ++ comboTheme.pack(side=RIGHT, anchor=E, padx=5, pady=5) + #frameHelp + frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y) +- frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH) ++ frameHelpList.pack(side=TOP,padx=5,pady=5,expand=True,fill=BOTH) + scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y) +- self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH) ++ self.listHelp.pack(side=LEFT,anchor=E,expand=True,fill=BOTH) + self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5) + self.buttonHelpListAdd.pack(side=TOP,anchor=W) + self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5) ++ + return frame + + def AttachVarCallbacks(self): +@@ -432,6 +513,7 @@ + self.fontBold.trace_variable('w',self.VarChanged_fontBold) + self.spaceNum.trace_variable('w',self.VarChanged_spaceNum) + self.colour.trace_variable('w',self.VarChanged_colour) ++ #self.themeFontBold.trace_variable('w', self.VarChanged_themeFontBold) + self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme) + self.customTheme.trace_variable('w',self.VarChanged_customTheme) + self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin) +@@ -446,18 +528,20 @@ + self.startupEdit.trace_variable('w',self.VarChanged_startupEdit) + self.autoSave.trace_variable('w',self.VarChanged_autoSave) + self.encoding.trace_variable('w',self.VarChanged_encoding) ++ self.themename.trace_variable('w', self.VarChanged_themename) ++ self.fileintab.trace_variable('w', self.VarChanged_fileintab) + + def VarChanged_fontSize(self,*params): + value=self.fontSize.get() +- self.AddChangedItem('main','EditorWindow','font-size',value) ++ self.AddChangedItem('main','EditorPage','font-size',value) + + def VarChanged_fontName(self,*params): + value=self.fontName.get() +- self.AddChangedItem('main','EditorWindow','font',value) ++ self.AddChangedItem('main','EditorPage','font',value) + + def VarChanged_fontBold(self,*params): + value=self.fontBold.get() +- self.AddChangedItem('main','EditorWindow','font-bold',value) ++ self.AddChangedItem('main','EditorPage','font-bold',value) + + def VarChanged_spaceNum(self,*params): + value=self.spaceNum.get() +@@ -466,6 +550,9 @@ + def VarChanged_colour(self,*params): + self.OnNewColourSet() + ++ #def VarChanged_themeFontBold(self, *params): ++ # self.OnBoldChanged() ++ + def VarChanged_builtinTheme(self,*params): + value=self.builtinTheme.get() + self.AddChangedItem('main','Theme','name',value) +@@ -521,11 +608,11 @@ + + def VarChanged_winWidth(self,*params): + value=self.winWidth.get() +- self.AddChangedItem('main','EditorWindow','width',value) ++ self.AddChangedItem('main', 'EditorWindow', 'width', value) + + def VarChanged_winHeight(self,*params): + value=self.winHeight.get() +- self.AddChangedItem('main','EditorWindow','height',value) ++ self.AddChangedItem('main', 'EditorWindow', 'height', value) + + def VarChanged_paraWidth(self,*params): + value=self.paraWidth.get() +@@ -541,8 +628,16 @@ + + def VarChanged_encoding(self,*params): + value=self.encoding.get() +- self.AddChangedItem('main','EditorWindow','encoding',value) ++ self.AddChangedItem('main','EditorPage','encoding',value) + ++ def VarChanged_themename(self, *params): ++ value = self.themename.get() ++ self.AddChangedItem('main', 'Theme', 'displaytheme', value) ++ ++ def VarChanged_fileintab(self, *params): ++ value = self.fileintab.get() ++ self.AddChangedItem('main', 'EditorWindow', 'file-in-tab', value) ++ + def ResetChangedItems(self): + #When any config item is changed in this dialog, an entry + #should be made in the relevant section (config type) of this +@@ -552,10 +647,10 @@ + self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}} + + def AddChangedItem(self,type,section,item,value): +- value=str(value) #make sure we use a string ++ value = str(value) #make sure we use a string + if not self.changedItems[type].has_key(section): +- self.changedItems[type][section]={} +- self.changedItems[type][section][item]=value ++ self.changedItems[type][section] = {} ++ self.changedItems[type][section][item] = value + + def GetDefaultItems(self): + dItems={'main':{},'highlight':{},'keys':{},'extensions':{}} +@@ -653,7 +748,7 @@ + newKeys={} + for event in prevKeys.keys(): #add key set to changed items + eventName=event[2:-2] #trim off the angle brackets +- binding=string.join(prevKeys[event]) ++ binding=' '.join(prevKeys[event]) + newKeys[eventName]=binding + #handle any unsaved changes to prev key set + if prevKeySetName in self.changedItems['keys'].keys(): +@@ -680,7 +775,7 @@ + bindNames.sort() + self.listBindings.delete(0,END) + for bindName in bindNames: +- key=string.join(keySet[bindName]) #make key(s) into a string ++ key=' '.join(keySet[bindName]) #make key(s) into a string + bindName=bindName[2:-2] #trim off the angle brackets + if keySetName in self.changedItems['keys'].keys(): + #handle any unsaved changes to this key set +@@ -694,9 +789,9 @@ + + def DeleteCustomKeys(self): + keySetName=self.customKeys.get() +- if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+ +- 'to delete the key set %r ?' % (keySetName), +- parent=self): ++ if not tkMessageBox.askyesno("Delete Key Set", ++ "Are you sure you wish to delete the key set %r ?" % keySetName, ++ parent=self): + return + #remove key set from config + idleConf.userCfg['keys'].remove_section(keySetName) +@@ -705,25 +800,26 @@ + #write changes + idleConf.userCfg['keys'].Save() + #reload user key set list +- itemList=idleConf.GetSectionList('user','keys') ++ itemList = idleConf.GetSectionList('user', 'keys') + itemList.sort() + if not itemList: + self.radioKeysCustom.config(state=DISABLED) +- self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -') ++ self.optMenuKeysCustom.SetMenu(itemList, "- no custom keys -") + else: + self.optMenuKeysCustom.SetMenu(itemList,itemList[0]) + #revert to default key set +- self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default')) +- self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name')) ++ self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys', ++ 'default')) ++ self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name')) + #user can't back out of these changes, they must be applied now + self.Apply() + self.SetKeysType() + + def DeleteCustomTheme(self): +- themeName=self.customTheme.get() +- if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+ +- 'to delete the theme %r ?' % (themeName,), +- parent=self): ++ themeName = self.customTheme.get() ++ if not tkMessageBox.askyesno("Delete Theme", ++ "Are you sure you wish to delete the theme %r ?" % themeName, ++ parent=self): + return + #remove theme from config + idleConf.userCfg['highlight'].remove_section(themeName) +@@ -732,30 +828,31 @@ + #write changes + idleConf.userCfg['highlight'].Save() + #reload user theme list +- itemList=idleConf.GetSectionList('user','highlight') ++ itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) +- self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -') ++ self.optMenuThemeCustom.SetMenu(itemList, "- no custom themes -") + else: +- self.optMenuThemeCustom.SetMenu(itemList,itemList[0]) ++ self.optMenuThemeCustom.SetMenu(itemList, itemList[0]) + #revert to default theme +- self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default')) +- self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name')) ++ self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme', ++ 'default')) ++ self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name')) + #user can't back out of these changes, they must be applied now + self.Apply() + self.SetThemeType() + + def GetColour(self): + target=self.highlightTarget.get() +- prevColour=self.frameColourSet.cget('bg') ++ prevColour = self.ttkstyle.configure('Color.TFrame', 'background') + rgbTuplet, colourString = tkColorChooser.askcolor(parent=self, + title='Pick new colour for : '+target,initialcolor=prevColour) + if colourString and (colourString!=prevColour): + #user didn't cancel, and they chose a new colour + if self.themeIsBuiltin.get(): #current theme is a built-in +- message=('Your changes will be saved as a new Custom Theme. '+ +- 'Enter a name for your new Custom Theme below.') ++ message=("Your changes will be saved as a new Custom Theme. " ++ "Enter a name for your new Custom Theme below.") + newTheme=self.GetNewThemeName(message) + if not newTheme: #user cancelled custom theme creation + return +@@ -767,16 +864,33 @@ + + def OnNewColourSet(self): + newColour=self.colour.get() +- self.frameColourSet.config(bg=newColour)#set sample ++ self.ttkstyle.configure('Color.TFrame', background=newColour) + if self.fgHilite.get(): plane='foreground' + else: plane='background' + sampleElement=self.themeElements[self.highlightTarget.get()][0] +- self.textHighlightSample.tag_config(sampleElement, **{plane:newColour}) ++ self.textHighlightSample.tag_config(sampleElement, **{plane: newColour}) + theme=self.customTheme.get() + themeElement=sampleElement+'-'+plane + self.AddChangedItem('highlight',theme,themeElement,newColour) + ++ #def OnBoldChanged(self): ++ # bold = self.themeFontBold.get() and tkFont.BOLD or tkFont.NORMAL ++ # sampleElement = self.themeElements[self.highlightTarget.get()][0] ++ ++ # #fontName = self.fontName.get() ++ # #self.textHighlightSample.tag_config(sampleElement, ++ # # font=(fontName, self.fontSize.get(), bold)) ++ ++ # self.textHighlightSample.tag_config(sampleElement, ++ # font=('courier', 12, bold)) ++ ++ # theme = self.customTheme.get() ++ # themeElement = "%s-%s" % (sampleElement, 'bold') ++ # self.AddChangedItem('highlight', theme, themeElement, ++ # (bold == tkFont.BOLD) and 1 or 0) ++ + def GetNewThemeName(self,message): ++ # XXX idle bug here + usedNames=(idleConf.GetSectionList('user','highlight')+ + idleConf.GetSectionList('default','highlight')) + newTheme=GetCfgSectionNameDialog(self,'New Custom Theme', +@@ -835,37 +949,45 @@ + self.radioFg.config(state=NORMAL) + self.radioBg.config(state=NORMAL) + self.fgHilite.set(1) ++ #tag = self.themeElements[self.highlightTarget.get()][0] ++ #font = self.textHighlightSample.tag_cget(tag, 'font') ++ #if font: ++ # self.themeFontBold.set(font.split()[2] == tkFont.BOLD) ++ #else: ++ # self.themeFontBold.set(0) + self.SetColourSample() + + def SetColourSampleBinding(self,*args): + self.SetColourSample() + + def SetColourSample(self): +- #set the colour smaple area +- tag=self.themeElements[self.highlightTarget.get()][0] +- if self.fgHilite.get(): plane='foreground' +- else: plane='background' +- colour=self.textHighlightSample.tag_cget(tag,plane) +- self.frameColourSet.config(bg=colour) ++ # set the colour sample area ++ tag = self.themeElements[self.highlightTarget.get()][0] ++ if self.fgHilite.get(): ++ plane = 'foreground' ++ else: ++ plane = 'background' ++ colour = self.textHighlightSample.tag_cget(tag, plane) ++ self.ttkstyle.configure('Color.TFrame', background=colour) + + def PaintThemeSample(self): + if self.themeIsBuiltin.get(): #a default theme +- theme=self.builtinTheme.get() ++ theme = self.builtinTheme.get() + else: #a user theme +- theme=self.customTheme.get() ++ theme = self.customTheme.get() + for elementTitle in self.themeElements.keys(): +- element=self.themeElements[elementTitle][0] +- colours=idleConf.GetHighlight(theme,element) +- if element=='cursor': #cursor sample needs special painting +- colours['background']=idleConf.GetHighlight(theme, +- 'normal', fgBg='bg') ++ element = self.themeElements[elementTitle][0] ++ colours = idleConf.GetHighlight(theme, element) ++ if element == 'cursor': #cursor sample needs special painting ++ colours['background'] = idleConf.GetHighlight(theme, ++ 'normal', fgBg='bg') + #handle any unsaved changes to this theme + if theme in self.changedItems['highlight'].keys(): +- themeDict=self.changedItems['highlight'][theme] +- if themeDict.has_key(element+'-foreground'): +- colours['foreground']=themeDict[element+'-foreground'] +- if themeDict.has_key(element+'-background'): +- colours['background']=themeDict[element+'-background'] ++ themeDict = self.changedItems['highlight'][theme] ++ if themeDict.has_key(element + '-foreground'): ++ colours['foreground'] = themeDict[element + '-foreground'] ++ if themeDict.has_key(element + '-background'): ++ colours['background'] = themeDict[element + '-background'] + self.textHighlightSample.tag_config(element, **colours) + self.SetColourSample() + +@@ -917,7 +1039,7 @@ + self.changedItems['main']['HelpFiles'] = {} + for num in range(1,len(self.userHelpList)+1): + self.AddChangedItem('main','HelpFiles',str(num), +- string.join(self.userHelpList[num-1][:2],';')) ++ ';'.join(self.userHelpList[num - 1][:2])) + + def LoadFontCfg(self): + ##base editor font selection list +@@ -925,7 +1047,7 @@ + fonts.sort() + for font in fonts: + self.listFontName.insert(END,font) +- configuredFont=idleConf.GetOption('main','EditorWindow','font', ++ configuredFont=idleConf.GetOption('main','EditorPage','font', + default='courier') + lc_configuredFont = configuredFont.lower() + self.fontName.set(lc_configuredFont) +@@ -936,13 +1058,13 @@ + self.listFontName.select_set(currentFontIndex) + self.listFontName.select_anchor(currentFontIndex) + ##font size dropdown +- fontSize=idleConf.GetOption('main','EditorWindow','font-size', ++ fontSize=idleConf.GetOption('main', 'EditorPage', 'font-size', + default='10') + self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14', + '16','18','20','22'),fontSize ) + ##fontWeight +- self.fontBold.set(idleConf.GetOption('main','EditorWindow', +- 'font-bold',default=0,type='bool')) ++ self.fontBold.set(idleConf.GetOption('main', 'EditorPage', ++ 'font-bold', default=0, type='bool')) + ##font sample + self.SetFontSample() + +@@ -954,37 +1076,41 @@ + + def LoadThemeCfg(self): + ##current theme type radiobutton +- self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default', +- type='bool',default=1)) ++ self.themeIsBuiltin.set(idleConf.GetOption('main', 'Theme', 'default', ++ type='bool', default=1)) + ##currently set theme +- currentOption=idleConf.CurrentTheme() ++ currentOption = idleConf.CurrentTheme() + ##load available theme option menus + if self.themeIsBuiltin.get(): #default theme selected +- itemList=idleConf.GetSectionList('default','highlight') ++ itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() +- self.optMenuThemeBuiltin.SetMenu(itemList,currentOption) +- itemList=idleConf.GetSectionList('user','highlight') ++ self.optMenuThemeBuiltin.SetMenu(itemList, currentOption) ++ itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() + if not itemList: + self.radioThemeCustom.config(state=DISABLED) +- self.customTheme.set('- no custom themes -') ++ self.customTheme.set("- no custom themes -") + else: + self.optMenuThemeCustom.SetMenu(itemList,itemList[0]) + else: #user theme selected +- itemList=idleConf.GetSectionList('user','highlight') ++ itemList = idleConf.GetSectionList('user', 'highlight') + itemList.sort() +- self.optMenuThemeCustom.SetMenu(itemList,currentOption) +- itemList=idleConf.GetSectionList('default','highlight') ++ self.optMenuThemeCustom.SetMenu(itemList, currentOption) ++ itemList = idleConf.GetSectionList('default', 'highlight') + itemList.sort() +- self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0]) ++ self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0]) + self.SetThemeType() + ##load theme element option menu +- themeNames=self.themeElements.keys() ++ themeNames = self.themeElements.keys() + themeNames.sort(self.__ThemeNameIndexCompare) +- self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0]) ++ self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0]) + self.PaintThemeSample() + self.SetHighlightTarget() + ++ if TTK: ++ displaytheme = idleConf.GetOption('main', 'Theme', 'displaytheme') ++ self.themename.set(displaytheme) ++ + def __ThemeNameIndexCompare(self,a,b): + if self.themeElements[a][1]>'), + ('Run Module', '<>'), ]), ] + +- def __init__(self, editwin): +- self.editwin = editwin ++ def __init__(self, editpage): ++ self.editpage = editpage ++ self.editwin = editpage.editwin + # Provide instance variables referenced by Debugger + # XXX This should be done differently + self.flist = self.editwin.flist +@@ -70,7 +71,7 @@ + msgtxt, (lineno, start) = msg + self.editwin.gotoline(lineno) + self.errorbox("Tabnanny Tokenizing Error", +- "Token Error: %s" % msgtxt) ++ "Token Error: %s" % msgtxt) + return False + except tabnanny.NannyNag, nag: + # The error messages from tabnanny are too confusing... +@@ -91,7 +92,7 @@ + source = re.sub(r"\r", "\n", source) + if source and source[-1] != '\n': + source = source + '\n' +- text = self.editwin.text ++ text = self.editpage.text + text.tag_remove("ERROR", "1.0", "end") + try: + try: +@@ -113,7 +114,7 @@ + shell.set_warning_stream(saved_stream) + + def colorize_syntax_error(self, msg, lineno, offset): +- text = self.editwin.text ++ text = self.editpage.text + pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1) + text.tag_add("ERROR", pos) + char = text.get(pos) +@@ -175,20 +176,20 @@ + + If the user has configured IDLE for Autosave, the file will be + silently saved if it already exists and is dirty. +- + """ +- filename = self.editwin.io.filename +- if not self.editwin.get_saved(): ++ page = self.editpage ++ filename = page.io.filename ++ if not page.get_saved(): + autosave = idleConf.GetOption('main', 'General', + 'autosave', type='bool') + if autosave and filename: +- self.editwin.io.save(None) ++ page.io.save(None) + else: + reply = self.ask_save_dialog() +- self.editwin.text.focus_set() ++ page.text.focus_set() + if reply == "ok": +- self.editwin.io.save(None) +- filename = self.editwin.io.filename ++ page.io.save(None) ++ filename = page.io.filename + else: + filename = None + return filename +@@ -196,14 +197,12 @@ + def ask_save_dialog(self): + msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?" + mb = tkMessageBox.Message(title="Save Before Run or Check", +- message=msg, +- icon=tkMessageBox.QUESTION, +- type=tkMessageBox.OKCANCEL, +- default=tkMessageBox.OK, +- master=self.editwin.text) ++ message=msg, icon=tkMessageBox.QUESTION, ++ type=tkMessageBox.OKCANCEL, default=tkMessageBox.OK, ++ master=self.editpage.text) + return mb.show() + + def errorbox(self, title, message): + # XXX This should really be a function of EditorWindow... +- tkMessageBox.showerror(title, message, master=self.editwin.text) +- self.editwin.text.focus_set() ++ tkMessageBox.showerror(title, message, master=self.editpage.text) ++ self.editpage.text.focus_set() +Index: tabbedpages.py +=================================================================== +--- tabbedpages.py (revision 63995) ++++ tabbedpages.py (revision 65541) +@@ -1,490 +1,12 @@ +-"""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. +-TabSet -- A widget containing tabs (buttons) in one or more rows. +- +-""" +-from Tkinter import * +- ++# Exceptions used on both versions of tabbedpages + class InvalidNameError(Exception): pass + class AlreadyExistsError(Exception): pass + ++def get_tabbedpage(): ++ """Returns the TabbedPageSet available for use.""" ++ try: ++ from tabbedpages_new import TabbedPageSet ++ except ImportError: ++ from tabbedpages_old import TabbedPageSet + +-class TabSet(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 TabSet 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 TabSet. 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 named """ +- 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 set_selected_tab(self, tab_name): +- """Show the tab named as the selected one""" +- 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) +- self._tab_rows.append(tab_row) +- +- for tab_name in tab_names: +- tab = TabSet.TabButton(tab_name, self.select_command, +- tab_row, self) +- 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 the last one created in the above loop +- 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 +- +- # not expanding the tabs with more than one row is very ugly +- expand_tabs = self.expand_tabs or n_rows > 1 +- i = 0 # index in self._tab_names +- 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.set_selected_tab(None) +- if selected in self._tab_names: +- self.set_selected_tab(selected) +- +- class TabButton(Frame): +- """A simple tab-like widget.""" +- +- bw = 2 # borderwidth +- +- def __init__(self, name, select_command, tab_row, tab_set): +- """Constructor arguments: +- +- name -- The tab's name, which will appear in its button. +- +- select_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, relief=RAISED) +- +- self.name = name +- self.select_command = select_command +- self.tab_set = tab_set +- self.is_last_in_row = False +- +- self.button = Radiobutton( +- self, text=name, command=self._select_event, +- padx=5, pady=1, takefocus=FALSE, indicatoron=FALSE, +- highlightthickness=0, selectcolor='', borderwidth=0) +- self.button.pack(side=LEFT, fill=X, expand=True) +- +- self._init_masks() +- self.set_normal() +- +- def _select_event(self, *args): +- """Event handler for tab selection. +- +- With TabbedPageSet, this calls TabbedPageSet.change_page, so that +- selecting a tab changes the page. +- +- Note that this does -not- call set_selected -- it will be called by +- TabSet.set_selected_tab, which should be called when whatever the +- tabs are related to changes. +- +- """ +- self.select_command(self.name) +- return +- +- def set_selected(self): +- """Assume selected look""" +- self._place_masks(selected=True) +- +- def set_normal(self): +- """Assume normal look""" +- 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 TabSet which will +- manage the tabs. See TabSet'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 = TabSet(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.set_selected_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() ++ return TabbedPageSet +Index: keybindingDialog.py +=================================================================== +--- keybindingDialog.py (revision 63995) ++++ keybindingDialog.py (revision 65541) +@@ -1,10 +1,18 @@ + """ + Dialog for building Tkinter accelerator key bindings + """ +-from Tkinter import * ++from Tkinter import Toplevel, Frame, Entry, Button, Checkbutton, Label, \ ++ Listbox, Scrollbar, StringVar ++from Tkconstants import TOP, BOTH, BOTTOM, X, NSEW, SUNKEN, LEFT, GROOVE, W, \ ++ END, EW, NS, SINGLE, VERTICAL, ANCHOR, MOVETO + import tkMessageBox + import string + ++from idlelib.configHandler import idleConf ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Frame, Entry, Button, Checkbutton, Label, Scrollbar ++ + class GetKeysDialog(Toplevel): + def __init__(self,parent,title,action,currentKeySequences): + """ +@@ -15,7 +23,7 @@ + """ + Toplevel.__init__(self, parent) + self.configure(borderwidth=5) +- self.resizable(height=FALSE,width=FALSE) ++ self.resizable(height=False, width=False) + self.title(title) + self.transient(parent) + self.grab_set() +@@ -46,10 +54,10 @@ + self.wait_window() + + def CreateWidgets(self): +- frameMain = Frame(self,borderwidth=2,relief=SUNKEN) +- frameMain.pack(side=TOP,expand=TRUE,fill=BOTH) ++ frameMain = Frame(self, borderwidth=2, relief=SUNKEN) ++ frameMain.pack(side=TOP, expand=True, fill=BOTH) + frameButtons=Frame(self) +- frameButtons.pack(side=BOTTOM,fill=X) ++ frameButtons.pack(side=BOTTOM, fill=X) + self.buttonOK = Button(frameButtons,text='OK', + width=8,command=self.OK) + self.buttonOK.grid(row=0,column=0,padx=5,pady=5) +@@ -124,6 +132,9 @@ + "separated by a space, eg., ." ) + labelHelpAdvanced.grid(row=0,column=0,sticky=NSEW) + ++ if TTK: ++ frameButtons['style'] = 'RootColor.TFrame' ++ + def SetModifiersForPlatform(self): + """Determine list of names of key modifiers for this platform. + +@@ -163,7 +174,7 @@ + if finalKey: + finalKey = self.TranslateKey(finalKey, modifiers) + keyList.append(finalKey) +- self.keyString.set('<' + string.join(keyList,'-') + '>') ++ self.keyString.set('<%s>' % '-'.join(keyList)) + + def GetModifiers(self): + modList = [variable.get() for variable in self.modifier_vars] +@@ -258,6 +269,7 @@ + return keysOK + + if __name__ == '__main__': ++ from Tkinter import Tk + #test the dialog + root=Tk() + def run(): +Index: configHelpSourceEdit.py +=================================================================== +--- configHelpSourceEdit.py (revision 63995) ++++ configHelpSourceEdit.py (revision 65541) +@@ -1,12 +1,17 @@ + "Dialog to specify or edit the parameters for a user configured help source." +- + import os + import sys +- +-from Tkinter import * ++from Tkinter import Toplevel, Frame, Entry, Button, Label, StringVar ++from Tkconstants import GROOVE, LEFT, RIGHT, W, ACTIVE, X, BOTH, TOP, BOTTOM + import tkMessageBox + import tkFileDialog + ++from configHandler import idleConf ++ ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Frame, Entry, Button, Label ++ + class GetHelpSourceDialog(Toplevel): + def __init__(self, parent, title, menuItem='', filePath=''): + """Get menu entry and url/ local file location for Additional Help +@@ -18,13 +23,14 @@ + """ + Toplevel.__init__(self, parent) + self.configure(borderwidth=5) +- self.resizable(height=FALSE, width=FALSE) ++ self.resizable(height=False, width=False) + self.title(title) + self.transient(parent) + self.grab_set() + self.protocol("WM_DELETE_WINDOW", self.Cancel) + self.parent = parent + self.result = None ++ + self.CreateWidgets() + self.menu.set(menuItem) + self.path.set(filePath) +@@ -46,33 +52,36 @@ + self.path = StringVar(self) + self.fontSize = StringVar(self) + self.frameMain = Frame(self, borderwidth=2, relief=GROOVE) +- self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) + labelMenu = Label(self.frameMain, anchor=W, justify=LEFT, +- text='Menu Item:') +- self.entryMenu = Entry(self.frameMain, textvariable=self.menu, +- width=30) +- self.entryMenu.focus_set() ++ text='Menu Item:') ++ self.entryMenu = Entry(self.frameMain, textvariable=self.menu) + labelPath = Label(self.frameMain, anchor=W, justify=LEFT, +- text='Help File Path: Enter URL or browse for file') ++ text='Help File Path: Enter URL or browse for file') + self.entryPath = Entry(self.frameMain, textvariable=self.path, +- width=40) ++ width=30) ++ browseButton = Button(self.frameMain, text='Browse', width=8, ++ command=self.browseFile) ++ frameButtons = Frame(self) ++ self.buttonOk = Button(frameButtons, text='OK', width=8, ++ default=ACTIVE, command=self.Ok) ++ self.buttonCancel = Button(frameButtons, text='Cancel', width=8, ++ command=self.Cancel) ++ + self.entryMenu.focus_set() ++ ++ self.frameMain.pack(side=TOP, expand=True, fill=BOTH) + labelMenu.pack(anchor=W, padx=5, pady=3) +- self.entryMenu.pack(anchor=W, padx=5, pady=3) ++ self.entryMenu.pack(anchor=W, padx=5, pady=3, fill=X) + labelPath.pack(anchor=W, padx=5, pady=3) +- self.entryPath.pack(anchor=W, padx=5, pady=3) +- browseButton = Button(self.frameMain, text='Browse', width=8, +- command=self.browseFile) +- browseButton.pack(pady=3) +- frameButtons = Frame(self) ++ self.entryPath.pack(anchor=W, padx=5, pady=3, side=LEFT, fill=X) ++ browseButton.pack(pady=3, padx=5, side=RIGHT) + frameButtons.pack(side=BOTTOM, fill=X) +- self.buttonOk = Button(frameButtons, text='OK', +- width=8, default=ACTIVE, command=self.Ok) +- self.buttonOk.grid(row=0, column=0, padx=5,pady=5) +- self.buttonCancel = Button(frameButtons, text='Cancel', +- width=8, command=self.Cancel) +- self.buttonCancel.grid(row=0, column=1, padx=5, pady=5) ++ self.buttonOk.pack(pady=5, side=RIGHT) ++ self.buttonCancel.pack(padx=5, pady=5, side=RIGHT) + ++ if TTK: ++ frameButtons['style'] = 'RootColor.TFrame' ++ + def browseFile(self): + filetypes = [ + ("HTML Files", "*.htm *.html", "TEXT"), +@@ -159,6 +168,7 @@ + self.destroy() + + if __name__ == '__main__': ++ from Tkinter import Tk + #test the dialog + root = Tk() + def run(): +Index: WidgetRedirector.py +=================================================================== +--- WidgetRedirector.py (revision 63995) ++++ WidgetRedirector.py (revision 65541) +@@ -1,4 +1,4 @@ +-from Tkinter import * ++from Tkinter import TclError + + class WidgetRedirector: + +@@ -105,6 +105,7 @@ + + + def main(): ++ from Tkinter import Tk, Text + root = Tk() + root.wm_protocol("WM_DELETE_WINDOW", root.quit) + text = Text() +Index: GrepDialog.py +=================================================================== +--- GrepDialog.py (revision 63995) ++++ GrepDialog.py (revision 65541) +@@ -1,10 +1,15 @@ + import os +-import fnmatch + import sys +-from Tkinter import * ++import fnmatch ++from Tkinter import StringVar, BooleanVar, Checkbutton ++ + import SearchEngine + from SearchDialogBase import SearchDialogBase ++from configHandler import idleConf + ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Checkbutton ++ + def grep(text, io=None, flist=None): + root = text._root() + engine = SearchEngine.get(root) +@@ -15,10 +20,10 @@ + dialog.open(text, searchphrase, io) + + class GrepDialog(SearchDialogBase): +- + title = "Find in Files Dialog" + icon = "Grep" + needwrapbutton = 0 ++ bottom_btns = [("Search Files", 'default_command', 1)] + + def __init__(self, root, engine, flist): + SearchDialogBase.__init__(self, root, engine) +@@ -40,20 +45,18 @@ + + def create_entries(self): + SearchDialogBase.create_entries(self) +- self.globent = self.make_entry("In files:", self.globvar) ++ self.globent = self.make_entry("In files", self.globvar) + + def create_other_buttons(self): + f = self.make_frame() + +- btn = Checkbutton(f, anchor="w", +- variable=self.recvar, ++ btn = Checkbutton(f, variable=self.recvar, + text="Recurse down subdirectories") + btn.pack(side="top", fill="both") +- btn.select() ++ btn.invoke() + + def create_command_buttons(self): + SearchDialogBase.create_command_buttons(self) +- self.make_button("Search Files", self.default_command, 1) + + def default_command(self, event=None): + prog = self.engine.getprog() +@@ -126,8 +129,3 @@ + for subdir in subdirs: + list.extend(self.findfiles(subdir, base, rec)) + return list +- +- def close(self, event=None): +- if self.top: +- self.top.grab_release() +- self.top.withdraw() +Index: FormatParagraph.py +=================================================================== +--- FormatParagraph.py (revision 63995) ++++ FormatParagraph.py (revision 65541) +@@ -25,28 +25,30 @@ + ]) + ] + +- def __init__(self, editwin): +- self.editwin = editwin ++ def __init__(self, editpage): ++ self.editpage = editpage + + def close(self): +- self.editwin = None ++ self.editpage = None + + def format_paragraph_event(self, event): +- maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph')) +- text = self.editwin.text +- first, last = self.editwin.get_selection_indices() ++ maxformatwidth = int(idleConf.GetOption('main', 'FormatParagraph', ++ 'paragraph')) ++ text = self.editpage.text ++ first, last = self.editpage.get_selection_indices() + if first and last: + data = text.get(first, last) + comment_header = '' + else: +- first, last, comment_header, data = \ +- find_paragraph(text, text.index("insert")) ++ first, last, comment_header, data = find_paragraph(text, ++ text.index("insert")) + if comment_header: + # Reformat the comment lines - convert to text sans header. + lines = data.split("\n") + lines = map(lambda st, l=len(comment_header): st[l:], lines) + data = "\n".join(lines) +- # Reformat to maxformatwidth chars or a 20 char width, whichever is greater. ++ # Reformat to maxformatwidth chars or a 20 char width, whichever is ++ # greater. + format_width = max(maxformatwidth - len(comment_header), 20) + newdata = reformat_paragraph(data, format_width) + # re-split and re-insert the comment header. +Index: EditorWindow.py +=================================================================== +--- EditorWindow.py (revision 63995) ++++ EditorWindow.py (revision 65541) +@@ -1,55 +1,54 @@ +-import sys + import os + import re +-import imp +-from itertools import count +-from Tkinter import * +-import tkSimpleDialog ++import sys ++import traceback ++import webbrowser + import tkMessageBox +-from MultiCall import MultiCallCreator ++import tkSimpleDialog ++from Tkinter import Menu, Scrollbar, TclError, BooleanVar ++from Tkconstants import INSERT, END, RIGHT, BOTTOM, TOP, X, Y, BOTH + +-import webbrowser +-import idlever ++import macosxSupport ++import Bindings + import WindowList +-import SearchDialog +-import GrepDialog +-import ReplaceDialog +-import PyParse ++from editorpage import EditorPage, classifyws, filename_to_unicode ++from tabbedpages import get_tabbedpage + from configHandler import idleConf +-import aboutDialog, textView, configDialog +-import macosxSupport ++from MultiStatusBar import MultiStatusBar + ++TabbedPageSet = get_tabbedpage() ++ ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Scrollbar ++ + # The default tab setting for a Text widget, in average-width characters. + TK_TABWIDTH_DEFAULT = 8 + +-def _find_module(fullname, path=None): +- """Version of imp.find_module() that handles hierarchical module names""" +- +- file = None +- for tgt in fullname.split('.'): +- if file is not None: +- file.close() # close intermediate files +- (file, filename, descr) = imp.find_module(tgt, path) +- if descr[2] == imp.PY_SOURCE: +- break # find but not load the source file +- module = imp.load_module(tgt, file, filename, descr) +- try: +- path = module.__path__ +- except AttributeError: +- raise ImportError, 'No source for module ' + module.__name__ +- return file, filename, descr +- + class EditorWindow(object): +- from Percolator import Percolator +- from ColorDelegator import ColorDelegator +- from UndoDelegator import UndoDelegator +- from IOBinding import IOBinding, filesystemencoding, encoding +- import Bindings +- from Tkinter import Toplevel +- from MultiStatusBar import MultiStatusBar ++ from ColorDelegator import ColorDelegator # overridden by PyShell ++ from UndoDelegator import UndoDelegator # overridden by PyShell + + help_url = None ++ rmenu = None ++ rmenu_specs = [ ++ # ("Label", "<>"), ... ++ ("Close", "<>"), # Example ++ ] ++ menu_specs = [ ++ ("file", "_File"), ++ ("edit", "_Edit"), ++ ("format", "F_ormat"), ++ ("run", "_Run"), ++ ("options", "_Options"), ++ ("windows", "_Windows"), ++ ("help", "_Help"), ++ ] + ++ if macosxSupport.runningAsOSXApp(): ++ del menu_specs[-3] ++ menu_specs[-2] = ("windows", "_Window") ++ + def __init__(self, flist=None, filename=None, key=None, root=None): + if EditorWindow.help_url is None: + dochome = os.path.join(sys.prefix, 'Doc', 'index.html') +@@ -81,7 +80,7 @@ + EditorWindow.help_url = 'file://' + EditorWindow.help_url + else: + EditorWindow.help_url = "http://www.python.org/doc/current" +- currentTheme=idleConf.CurrentTheme() ++ + self.flist = flist + root = root or flist.root + self.root = root +@@ -90,94 +89,45 @@ + except AttributeError: + sys.ps1 = '>>> ' + self.menubar = Menu(root) +- self.top = top = WindowList.ListedToplevel(root, menu=self.menubar) ++ self.top = WindowList.ListedToplevel(root, menu=self.menubar) + if flist: + self.tkinter_vars = flist.vars +- #self.top.instance_dict makes flist.inversedict avalable to +- #configDialog.py so it can access all EditorWindow instaces ++ # self.top.instance_dict makes flist.inversedict avalable to ++ # configDialog.py so it can access all EditorWindow instaces + self.top.instance_dict = flist.inversedict + else: + self.tkinter_vars = {} # keys: Tkinter event names + # values: Tkinter variable instances + self.top.instance_dict = {} + self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(), +- 'recent-files.lst') +- self.text_frame = text_frame = Frame(top) +- self.vbar = vbar = Scrollbar(text_frame, name='vbar') +- self.width = idleConf.GetOption('main','EditorWindow','width') +- self.text = text = MultiCallCreator(Text)( +- text_frame, name='text', padx=5, wrap='none', +- width=self.width, +- height=idleConf.GetOption('main','EditorWindow','height') ) +- self.top.focused_widget = self.text ++ 'recent-files.lst') + +- self.createmenubar() +- self.apply_bindings() +- +- self.top.protocol("WM_DELETE_WINDOW", self.close) +- self.top.bind("<>", self.close_event) +- if macosxSupport.runningAsOSXApp(): +- # Command-W on editorwindows doesn't work without this. +- text.bind('<>', self.close_event) +- text.bind("<>", self.cut) +- text.bind("<>", self.copy) +- text.bind("<>", self.paste) +- text.bind("<>", self.center_insert_event) +- text.bind("<>", self.help_dialog) +- text.bind("<>", self.python_docs) +- text.bind("<>", self.about_dialog) +- text.bind("<>", self.config_dialog) +- text.bind("<>", self.open_module) +- text.bind("<>", lambda event: "break") +- text.bind("<>", self.select_all) +- text.bind("<>", self.remove_selection) +- text.bind("<>", self.find_event) +- text.bind("<>", self.find_again_event) +- text.bind("<>", self.find_in_files_event) +- text.bind("<>", self.find_selection_event) +- text.bind("<>", self.replace_event) +- text.bind("<>", self.goto_line_event) +- text.bind("<3>", self.right_menu_event) +- text.bind("<>",self.smart_backspace_event) +- text.bind("<>",self.newline_and_indent_event) +- text.bind("<>",self.smart_indent_event) +- text.bind("<>",self.indent_region_event) +- text.bind("<>",self.dedent_region_event) +- text.bind("<>",self.comment_region_event) +- text.bind("<>",self.uncomment_region_event) +- text.bind("<>",self.tabify_region_event) +- text.bind("<>",self.untabify_region_event) +- text.bind("<>",self.toggle_tabs_event) +- text.bind("<>",self.change_indentwidth_event) +- text.bind("", self.move_at_edge_if_selection(0)) +- text.bind("", self.move_at_edge_if_selection(1)) +- text.bind("<>", self.del_word_left) +- text.bind("<>", self.del_word_right) +- text.bind("<>", self.home_callback) +- + if flist: + flist.inversedict[self] = key + if key: + flist.dict[key] = self +- text.bind("<>", self.new_callback) +- text.bind("<>", self.flist.close_all_callback) +- text.bind("<>", self.open_class_browser) +- text.bind("<>", self.open_path_browser) + +- self.set_status_bar() +- vbar['command'] = text.yview +- vbar.pack(side=RIGHT, fill=Y) +- text['yscrollcommand'] = vbar.set +- fontWeight = 'normal' +- if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'): +- fontWeight='bold' +- text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'), +- idleConf.GetOption('main', 'EditorWindow', 'font-size'), +- fontWeight)) +- text_frame.pack(side=LEFT, fill=BOTH, expand=1) +- text.pack(side=TOP, fill=BOTH, expand=1) +- text.focus_set() ++ self.menudict = None + ++ # create a Notebook where the text pages for this EditorWindow will ++ # reside ++ self.text_notebook = TabbedPageSet(self.top) ++ self.text_notebook.pack(fill=BOTH, expand=True) ++ self.text_notebook.bind('<>', self._update_controls) ++ self.new_tab(filename=filename, load_ext=False) ++ self.text = self.current_page.text # XXX ++ self.top.focused_widget = self.text ++ self.top.bind('<>', self._post_tab_close) ++ ++ # The following "width" attribute is used by PyShell, so keep it here ++ self.width = idleConf.GetOption('main', 'EditorPage', 'width') ++ ++ self.top.protocol("WM_DELETE_WINDOW", self.close) ++ self.top.bind("<>", self.close_event) ++ ++ self._create_statusbar() ++ self.top.after_idle(self.set_line_and_column) ++ + # usetabs true -> literal tab characters are used by indent and + # dedent cmds, possibly mixed with spaces if + # indentwidth is not a multiple of tabwidth, +@@ -187,7 +137,8 @@ + # Although use-spaces=0 can be configured manually in config-main.def, + # configuration of tabs v. spaces is not supported in the configuration + # dialog. IDLE promotes the preferred Python indentation: use spaces! +- usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool') ++ usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', ++ type='bool') + self.usetabs = not usespaces + + # tabwidth is the display width of a literal tab character. +@@ -214,37 +165,10 @@ + # Making the initial values larger slows things down more often. + self.num_context_lines = 50, 500, 5000000 + +- self.per = per = self.Percolator(text) +- +- self.undo = undo = self.UndoDelegator() +- per.insertfilter(undo) +- text.undo_block_start = undo.undo_block_start +- text.undo_block_stop = undo.undo_block_stop +- undo.set_saved_change_hook(self.saved_change_hook) +- +- # IOBinding implements file I/O and printing functionality +- self.io = io = self.IOBinding(self) +- io.set_filename_change_hook(self.filename_change_hook) +- +- # Create the recent files submenu +- self.recent_files_menu = Menu(self.menubar) +- self.menudict['file'].insert_cascade(3, label='Recent Files', +- underline=0, +- menu=self.recent_files_menu) +- self.update_recent_files_list() +- +- self.color = None # initialized below in self.ResetColorizer +- if filename: +- if os.path.exists(filename) and not os.path.isdir(filename): +- io.loadfile(filename) +- else: +- io.set_filename(filename) +- self.ResetColorizer() +- self.saved_change_hook() +- + self.set_indentation_params(self.ispythonsource(filename)) + +- self.load_extensions() ++ self.extensions = {} ++ self._load_extensions() + + menu = self.menudict.get('windows') + if menu: +@@ -262,121 +186,96 @@ + self.askinteger = tkSimpleDialog.askinteger + self.showerror = tkMessageBox.showerror + +- def _filename_to_unicode(self, filename): +- """convert filename to unicode in order to display it in Tk""" +- if isinstance(filename, unicode) or not filename: +- return filename ++ @property ++ def current_page(self): ++ """Return the active EditorPage in EditorWindow.""" ++ curr_tab = self.text_notebook.select() ++ if not curr_tab: ++ return None ++ ++ if TTK: ++ page = self.text_notebook.pages[self.text_notebook.tab( ++ curr_tab)['text']].editpage + else: +- try: +- return filename.decode(self.filesystemencoding) +- except UnicodeDecodeError: +- # XXX +- try: +- return filename.decode(self.encoding) +- except UnicodeDecodeError: +- # byte-to-byte conversion +- return filename.decode('iso8859-1') ++ page = self.text_notebook.pages[curr_tab].editpage ++ return page + +- def new_callback(self, event): +- dirname, basename = self.io.defaultfilename() +- self.flist.new(dirname) +- return "break" ++ def short_title(self): ++ # overriden by PyShell ++ self.current_page.short_title() + +- def home_callback(self, event): +- if (event.state & 12) != 0 and event.keysym == "Home": +- # state&1==shift, state&4==control, state&8==alt +- return # ; fall back to class binding ++ def next_tab(self, event): ++ """Show next tab if not in the last tab already.""" ++ index = self.text_notebook.index(self.text_notebook.select()) ++ if index == len(self.text_notebook.tabs()) - 1: ++ return ++ self.text_notebook.select(index + 1) + +- if self.text.index("iomark") and \ +- self.text.compare("iomark", "<=", "insert lineend") and \ +- self.text.compare("insert linestart", "<=", "iomark"): +- insertpt = int(self.text.index("iomark").split(".")[1]) +- else: +- line = self.text.get("insert linestart", "insert lineend") +- for insertpt in xrange(len(line)): +- if line[insertpt] not in (' ','\t'): +- break +- else: +- insertpt=len(line) ++ def prev_tab(self, event): ++ """Show the previous tab if not in the first tab already.""" ++ index = self.text_notebook.index(self.text_notebook.select()) ++ if index == 0: ++ return ++ self.text_notebook.select(index - 1) + +- lineat = int(self.text.index("insert").split('.')[1]) ++ def new_tab(self, event=None, filename=None, load_ext=True): ++ """Create a new EditorPage and insert it into the notebook.""" ++ page_title = "#%d" % (len(self.text_notebook.pages) + 1) ++ page = self.text_notebook.add_page(page_title) + +- if insertpt == lineat: +- insertpt = 0 ++ vbar = Scrollbar(page.frame, name='vbar') ++ page.editpage = EditorPage(page.frame, self, title=page_title, ++ name='text', padx=5, wrap='none') + +- dest = "insert linestart+"+str(insertpt)+"c" ++ firstpage = False # don't update window's title ++ if self.menudict is None: ++ # This EditorWindow is being created now, perform the following ++ # tasks before. ++ firstpage = True # will cause window's title to be updated ++ self.menudict = {} ++ self._createmenubar(page.editpage.text) ++ # Create the recent files submenu ++ self.recent_files_menu = Menu(self.menubar) ++ self.menudict['file'].insert_cascade(3, label='Recent Files', ++ underline=0, menu=self.recent_files_menu) ++ self.update_recent_files_list() + +- if (event.state&1) == 0: +- # shift not pressed +- self.text.tag_remove("sel", "1.0", "end") +- else: +- if not self.text.index("sel.first"): +- self.text.mark_set("anchor","insert") ++ page.editpage.post_init(filename=filename, update_window_title=firstpage) + +- first = self.text.index(dest) +- last = self.text.index("anchor") ++ text = page.editpage.text ++ vbar['command'] = text.yview ++ vbar.pack(side=RIGHT, fill=Y) ++ text['yscrollcommand'] = vbar.set ++ fontWeight = 'normal' ++ if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'): ++ fontWeight = 'bold' ++ text.config(font=(idleConf.GetOption('main', 'EditorPage', 'font'), ++ idleConf.GetOption('main', 'EditorPage', 'font-size'), ++ fontWeight)) ++ text.pack(side=TOP, fill=BOTH, expand=1) ++ text.focus_set() + +- if self.text.compare(first,">",last): +- first,last = last,first ++ self.apply_bindings(tab=page) ++ if load_ext: ++ self._load_extensions() ++ self.top.event_generate('<>') ++ return "break" + +- self.text.tag_remove("sel", "1.0", "end") +- self.text.tag_add("sel", first, last) +- +- self.text.mark_set("insert", dest) +- self.text.see("insert") ++ def new_callback(self, event, page): ++ dirname, basename = page.io.defaultfilename() ++ self.flist.new(dirname) + return "break" + +- def set_status_bar(self): +- self.status_bar = self.MultiStatusBar(self.top) +- if macosxSupport.runningAsOSXApp(): +- # Insert some padding to avoid obscuring some of the statusbar +- # by the resize widget. +- self.status_bar.set_label('_padding1', ' ', side=RIGHT) +- self.status_bar.set_label('column', 'Col: ?', side=RIGHT) +- self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) +- self.status_bar.pack(side=BOTTOM, fill=X) +- self.text.bind("<>", self.set_line_and_column) +- self.text.event_add("<>", +- "", "") +- self.text.after_idle(self.set_line_and_column) +- + def set_line_and_column(self, event=None): +- line, column = self.text.index(INSERT).split('.') ++ # Used by PyShell too ++ curr_page = self.current_page ++ if not curr_page: ++ return ++ ++ line, column = curr_page.text.index(INSERT).split('.') + self.status_bar.set_label('column', 'Col: %s' % column) + self.status_bar.set_label('line', 'Ln: %s' % line) + +- menu_specs = [ +- ("file", "_File"), +- ("edit", "_Edit"), +- ("format", "F_ormat"), +- ("run", "_Run"), +- ("options", "_Options"), +- ("windows", "_Windows"), +- ("help", "_Help"), +- ] +- +- if macosxSupport.runningAsOSXApp(): +- del menu_specs[-3] +- menu_specs[-2] = ("windows", "_Window") +- +- +- def createmenubar(self): +- mbar = self.menubar +- self.menudict = menudict = {} +- for name, label in self.menu_specs: +- underline, label = prepstr(label) +- menudict[name] = menu = Menu(mbar, name=name) +- mbar.add_cascade(label=label, menu=menu, underline=underline) +- +- if sys.platform == 'darwin' and '.framework' in sys.executable: +- # Insert the application menu +- menudict['application'] = menu = Menu(mbar, name='apple') +- mbar.add_cascade(label='IDLE', menu=menu) +- +- self.fill_menus() +- self.base_helpmenu_length = self.menudict['help'].index(END) +- self.reset_help_menu_entries() +- + def postwindowsmenu(self): + # Only called when Windows menu exists + menu = self.menudict['windows'] +@@ -387,195 +286,27 @@ + menu.delete(self.wmenu_end+1, end) + WindowList.add_windows_to_menu(menu) + +- rmenu = None ++ def newline_and_indent_event(self, event): ++ """Call newline_and_indent_event on current EditorPage.""" ++ self.current_page.newline_and_indent_event(event) + +- def right_menu_event(self, event): +- self.text.tag_remove("sel", "1.0", "end") +- self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) +- if not self.rmenu: +- self.make_rmenu() +- rmenu = self.rmenu +- self.event = event +- iswin = sys.platform[:3] == 'win' +- if iswin: +- self.text.config(cursor="arrow") +- rmenu.tk_popup(event.x_root, event.y_root) +- if iswin: +- self.text.config(cursor="ibeam") ++ def get_selection_indices(self): ++ """Call get_selection_indices on current EditorPage.""" ++ return self.current_page.get_selection_indices() + +- rmenu_specs = [ +- # ("Label", "<>"), ... +- ("Close", "<>"), # Example +- ] ++ def build_char_in_string_func(self, startindex): ++ """Call build_char_in_string_func on current EditorPage.""" ++ return self.current_page.build_char_in_string_func(startindex) + +- def make_rmenu(self): +- rmenu = Menu(self.text, tearoff=0) +- for label, eventname in self.rmenu_specs: +- def command(text=self.text, eventname=eventname): +- text.event_generate(eventname) +- rmenu.add_command(label=label, command=command) +- self.rmenu = rmenu +- +- def about_dialog(self, event=None): +- aboutDialog.AboutDialog(self.top,'About IDLE') +- +- def config_dialog(self, event=None): +- configDialog.ConfigDialog(self.top,'Settings') +- +- def help_dialog(self, event=None): +- fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') +- textView.view_file(self.top,'Help',fn) +- +- def python_docs(self, event=None): +- if sys.platform[:3] == 'win': +- os.startfile(self.help_url) +- else: +- webbrowser.open(self.help_url) +- return "break" +- +- def cut(self,event): +- self.text.event_generate("<>") +- return "break" +- +- def copy(self,event): +- if not self.text.tag_ranges("sel"): +- # There is no selection, so do nothing and maybe interrupt. +- return +- self.text.event_generate("<>") +- return "break" +- +- def paste(self,event): +- self.text.event_generate("<>") +- self.text.see("insert") +- return "break" +- +- def select_all(self, event=None): +- self.text.tag_add("sel", "1.0", "end-1c") +- self.text.mark_set("insert", "1.0") +- self.text.see("insert") +- return "break" +- +- def remove_selection(self, event=None): +- self.text.tag_remove("sel", "1.0", "end") +- self.text.see("insert") +- +- def move_at_edge_if_selection(self, edge_index): +- """Cursor move begins at start or end of selection +- +- When a left/right cursor key is pressed create and return to Tkinter a +- function which causes a cursor move from the associated edge of the +- selection. +- +- """ +- self_text_index = self.text.index +- self_text_mark_set = self.text.mark_set +- edges_table = ("sel.first+1c", "sel.last-1c") +- def move_at_edge(event): +- if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed +- try: +- self_text_index("sel.first") +- self_text_mark_set("insert", edges_table[edge_index]) +- except TclError: +- pass +- return move_at_edge +- +- def del_word_left(self, event): +- self.text.event_generate('') +- return "break" +- +- def del_word_right(self, event): +- self.text.event_generate('') +- return "break" +- +- def find_event(self, event): +- SearchDialog.find(self.text) +- return "break" +- +- def find_again_event(self, event): +- SearchDialog.find_again(self.text) +- return "break" +- +- def find_selection_event(self, event): +- SearchDialog.find_selection(self.text) +- return "break" +- +- def find_in_files_event(self, event): +- GrepDialog.grep(self.text, self.io, self.flist) +- return "break" +- +- def replace_event(self, event): +- ReplaceDialog.replace(self.text) +- return "break" +- +- def goto_line_event(self, event): +- text = self.text +- lineno = tkSimpleDialog.askinteger("Goto", +- "Go to line number:",parent=text) +- if lineno is None: +- return "break" +- if lineno <= 0: +- text.bell() +- return "break" +- text.mark_set("insert", "%d.0" % lineno) +- text.see("insert") +- +- def open_module(self, event=None): +- # XXX Shouldn't this be in IOBinding or in FileList? +- try: +- name = self.text.get("sel.first", "sel.last") +- except TclError: +- name = "" +- else: +- name = name.strip() +- name = tkSimpleDialog.askstring("Module", +- "Enter the name of a Python module\n" +- "to search on sys.path and open:", +- parent=self.text, initialvalue=name) +- if name: +- name = name.strip() +- if not name: +- return +- # XXX Ought to insert current file's directory in front of path +- try: +- (f, file, (suffix, mode, type)) = _find_module(name) +- except (NameError, ImportError), msg: +- tkMessageBox.showerror("Import error", str(msg), parent=self.text) +- return +- if type != imp.PY_SOURCE: +- tkMessageBox.showerror("Unsupported type", +- "%s is not a source module" % name, parent=self.text) +- return +- if f: +- f.close() +- if self.flist: +- self.flist.open(file) +- else: +- self.io.loadfile(file) +- +- def open_class_browser(self, event=None): +- filename = self.io.filename +- if not filename: +- tkMessageBox.showerror( +- "No filename", +- "This buffer has no associated filename", +- master=self.text) +- self.text.focus_set() +- return None +- head, tail = os.path.split(filename) +- base, ext = os.path.splitext(tail) +- import ClassBrowser +- ClassBrowser.ClassBrowser(self.flist, base, [head]) +- +- def open_path_browser(self, event=None): +- import PathBrowser +- PathBrowser.PathBrowser(self.flist) +- + def gotoline(self, lineno): ++ page = self.current_page ++ text = page.text ++ + if lineno is not None and lineno > 0: +- self.text.mark_set("insert", "%d.0" % lineno) +- self.text.tag_remove("sel", "1.0", "end") +- self.text.tag_add("sel", "insert", "insert +1l") +- self.center() ++ text.mark_set("insert", "%d.0" % lineno) ++ text.tag_remove("sel", "1.0", "end") ++ text.tag_add("sel", "insert", "insert +1l") ++ page.center() + + def ispythonsource(self, filename): + if not filename or os.path.isdir(filename): +@@ -599,82 +330,59 @@ + def set_close_hook(self, close_hook): + self.close_hook = close_hook + +- def filename_change_hook(self): +- if self.flist: +- self.flist.filename_changed_edit(self) +- self.saved_change_hook() +- self.top.update_windowlist_registry(self) +- self.ResetColorizer() ++ def set_theme(self, ttkstyle): ++ # called from configDialog.py ++ ttkstyle.theme_use(idleConf.GetOption('main', 'Theme', 'displaytheme')) + +- def _addcolorizer(self): +- if self.color: +- return +- if self.ispythonsource(self.io.filename): +- self.color = self.ColorDelegator() +- # can add more colorizers here... +- if self.color: +- self.per.removefilter(self.undo) +- self.per.insertfilter(self.color) +- self.per.insertfilter(self.undo) +- +- def _rmcolorizer(self): +- if not self.color: +- return +- self.color.removecolors() +- self.per.removefilter(self.color) +- self.color = None +- + def ResetColorizer(self): + "Update the colour theme" + # Called from self.filename_change_hook and from configDialog.py +- self._rmcolorizer() +- self._addcolorizer() +- theme = idleConf.GetOption('main','Theme','name') +- normal_colors = idleConf.GetHighlight(theme, 'normal') +- cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') +- select_colors = idleConf.GetHighlight(theme, 'hilite') +- self.text.config( +- foreground=normal_colors['foreground'], +- background=normal_colors['background'], +- insertbackground=cursor_color, +- selectforeground=select_colors['foreground'], +- selectbackground=select_colors['background'], +- ) ++ for page in self.text_notebook.pages.itervalues(): ++ page.editpage.reset_colorizer() + + def ResetFont(self): + "Update the text widgets' font if it is changed" + # Called from configDialog.py +- fontWeight='normal' +- if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'): +- fontWeight='bold' +- self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'), +- idleConf.GetOption('main','EditorWindow','font-size'), ++ fontWeight = 'normal' ++ if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'): ++ fontWeight = 'bold' ++ ++ for page in self.text_notebook.pages.itervalues(): ++ text = page.editpage.text ++ text.config(font=(idleConf.GetOption('main', 'EditorPage', 'font'), ++ idleConf.GetOption('main', 'EditorPage', 'font-size'), + fontWeight)) + + def RemoveKeybindings(self): + "Remove the keybindings before they are changed." + # Called from configDialog.py +- self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() +- for event, keylist in keydefs.items(): +- self.text.event_delete(event, *keylist) +- for extensionName in self.get_standard_extension_names(): ++ Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() ++ ++ for page in self.text_notebook.pages.itervalues(): ++ text = page.editpage.text ++ for event, keylist in keydefs.items(): ++ text.event_delete(event, *keylist) ++ ++ for extensionName in self._get_standard_extension_names(): + xkeydefs = idleConf.GetExtensionBindings(extensionName) + if xkeydefs: +- for event, keylist in xkeydefs.items(): +- self.text.event_delete(event, *keylist) ++ for page in self.text_notebook.pages.itervalues(): ++ text = page.editpage.text ++ for event, keylist in xkeydefs.items(): ++ text.event_delete(event, *keylist) + + def ApplyKeybindings(self): + "Update the keybindings after they are changed" + # Called from configDialog.py +- self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() ++ Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet() + self.apply_bindings() +- for extensionName in self.get_standard_extension_names(): ++ for extensionName in self._get_standard_extension_names(): + xkeydefs = idleConf.GetExtensionBindings(extensionName) + if xkeydefs: + self.apply_bindings(xkeydefs) + #update menu accelerators + menuEventDict = {} +- for menu in self.Bindings.menudefs: ++ for menu in Bindings.menudefs: + menuEventDict[menu[0]] = {} + for item in menu[1]: + if item: +@@ -695,13 +403,6 @@ + accel = get_accelerator(keydefs, event) + menu.entryconfig(index, accelerator=accel) + +- def set_notabs_indentwidth(self): +- "Update the indentwidth if changed and not using tabs in this window" +- # Called from configDialog.py +- if not self.usetabs: +- self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces', +- type='int') +- + def reset_help_menu_entries(self): + "Update the additional help entries on the Help menu" + help_list = idleConf.GetAllExtraHelpSourcesList() +@@ -719,19 +420,16 @@ + # and update the menu dictionary + self.menudict['help'] = helpmenu + +- def __extra_help_callback(self, helpfile): +- "Create a callback with the helpfile value frozen at definition time" +- def display_extra_help(helpfile=helpfile): +- if not helpfile.startswith(('www', 'http')): +- url = os.path.normpath(helpfile) +- if sys.platform[:3] == 'win': +- os.startfile(helpfile) +- else: +- webbrowser.open(helpfile) +- return display_extra_help ++ def set_notabs_indentwidth(self): ++ "Update the indentwidth if changed and not using tabs in this window" ++ # Called from configDialog.py ++ if not self.usetabs: ++ self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces', ++ type='int') + + def update_recent_files_list(self, new_file=None): + "Load and update the recent files list and menus" ++ # IOBinding calls this + rf_list = [] + if os.path.exists(self.recent_files_path): + rf_list_file = open(self.recent_files_path,'r') +@@ -761,83 +459,18 @@ + for instance in self.top.instance_dict.keys(): + menu = instance.recent_files_menu + menu.delete(1, END) # clear, and rebuild: +- for i, file in zip(count(), rf_list): ++ for i, file in enumerate(rf_list): + file_name = file[0:-1] # zap \n + # make unicode string to display non-ASCII chars correctly +- ufile_name = self._filename_to_unicode(file_name) ++ ufile_name = filename_to_unicode(file_name) + callback = instance.__recent_file_callback(file_name) + menu.add_command(label=ulchars[i] + " " + ufile_name, + command=callback, + underline=0) + +- def __recent_file_callback(self, file_name): +- def open_recent_file(fn_closure=file_name): +- self.io.open(editFile=fn_closure) +- return open_recent_file +- +- def saved_change_hook(self): +- short = self.short_title() +- long = self.long_title() +- if short and long: +- title = short + " - " + long +- elif short: +- title = short +- elif long: +- title = long +- else: +- title = "Untitled" +- icon = short or long or title +- if not self.get_saved(): +- title = "*%s*" % title +- icon = "*%s" % icon +- self.top.wm_title(title) +- self.top.wm_iconname(icon) +- + def get_saved(self): +- return self.undo.get_saved() ++ return self.current_page.undo.get_saved() # XXX check this! + +- def set_saved(self, flag): +- self.undo.set_saved(flag) +- +- def reset_undo(self): +- self.undo.reset_undo() +- +- def short_title(self): +- filename = self.io.filename +- if filename: +- filename = os.path.basename(filename) +- # return unicode string to display non-ASCII chars correctly +- return self._filename_to_unicode(filename) +- +- def long_title(self): +- # return unicode string to display non-ASCII chars correctly +- return self._filename_to_unicode(self.io.filename or "") +- +- def center_insert_event(self, event): +- self.center() +- +- def center(self, mark="insert"): +- text = self.text +- top, bot = self.getwindowlines() +- lineno = self.getlineno(mark) +- height = bot - top +- newtop = max(1, lineno - height//2) +- text.yview(float(newtop)) +- +- def getwindowlines(self): +- text = self.text +- top = self.getlineno("@0,0") +- bot = self.getlineno("@0,65535") +- if top == bot and text.winfo_height() == 1: +- # Geometry manager hasn't run yet +- height = int(text['height']) +- bot = top + height - 1 +- return top, bot +- +- def getlineno(self, mark="insert"): +- text = self.text +- return int(float(text.index(mark))) +- + def get_geometry(self): + "Return (width, height, x, y)" + geom = self.top.wm_geometry() +@@ -848,132 +481,49 @@ + def close_event(self, event): + self.close() + +- def maybesave(self): +- if self.io: +- if not self.get_saved(): +- if self.top.state()!='normal': +- self.top.deiconify() +- self.top.lower() +- self.top.lift() +- return self.io.maybesave() +- + def close(self): +- reply = self.maybesave() +- if str(reply) != "cancel": +- self._close() +- return reply ++ to_check = self.text_notebook.pages.copy() + ++ while to_check: ++ curr_tab = self.text_notebook.select() ++ if TTK: ++ page_name = self.text_notebook.tab(curr_tab)['text'] ++ else: ++ page_name = curr_tab ++ page = to_check.pop(page_name) ++ editpage = page.editpage ++ reply = editpage.close_tab() ++ if reply == "cancel": ++ break ++ + def _close(self): +- if self.io.filename: +- self.update_recent_files_list(new_file=self.io.filename) + WindowList.unregister_callback(self.postwindowsmenu) +- self.unload_extensions() +- self.io.close() +- self.io = None +- self.undo = None +- if self.color: +- self.color.close(False) +- self.color = None +- self.text = None ++ self._unload_extensions() + self.tkinter_vars = None +- self.per.close() +- self.per = None ++ ++ for page in self.text_notebook.pages.itervalues(): ++ page.editpage.close() ++ + 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 = {} +- self.load_standard_extensions() +- +- def unload_extensions(self): +- for ins in self.extensions.values(): +- if hasattr(ins, "close"): +- ins.close() +- self.extensions = {} +- +- def load_standard_extensions(self): +- for name in self.get_standard_extension_names(): +- try: +- self.load_extension(name) +- except: +- print "Failed to load extension", repr(name) +- import traceback +- traceback.print_exc() +- +- def get_standard_extension_names(self): +- return idleConf.GetExtensions(editor_only=True) +- +- def load_extension(self, name): +- try: +- mod = __import__(name, globals(), locals(), []) +- except ImportError: +- print "\nFailed to import extension: ", name +- return +- cls = getattr(mod, name) +- keydefs = idleConf.GetExtensionBindings(name) +- if hasattr(cls, "menudefs"): +- self.fill_menus(cls.menudefs, keydefs) +- ins = cls(self) +- self.extensions[name] = ins +- if keydefs: +- self.apply_bindings(keydefs) +- for vevent in keydefs.keys(): +- methodname = vevent.replace("-", "_") +- while methodname[:1] == '<': +- methodname = methodname[1:] +- while methodname[-1:] == '>': +- methodname = methodname[:-1] +- methodname = methodname + "_event" +- if hasattr(ins, methodname): +- self.text.bind(vevent, getattr(ins, methodname)) +- +- def apply_bindings(self, keydefs=None): ++ def apply_bindings(self, keydefs=None, tab=None): + if keydefs is None: +- keydefs = self.Bindings.default_keydefs +- text = self.text +- text.keydefs = keydefs +- for event, keylist in keydefs.items(): +- if keylist: +- text.event_add(event, *keylist) ++ keydefs = Bindings.default_keydefs + +- def fill_menus(self, menudefs=None, keydefs=None): +- """Add appropriate entries to the menus and submenus ++ if tab: ++ iter_over = [tab] ++ else: ++ iter_over = self.text_notebook.pages.itervalues() + +- Menus that are absent or None in self.menudict are ignored. +- """ +- if menudefs is None: +- menudefs = self.Bindings.menudefs +- if keydefs is None: +- keydefs = self.Bindings.default_keydefs +- menudict = self.menudict +- text = self.text +- for mname, entrylist in menudefs: +- menu = menudict.get(mname) +- if not menu: +- continue +- for entry in entrylist: +- if not entry: +- menu.add_separator() +- else: +- label, eventname = entry +- checkbutton = (label[:1] == '!') +- if checkbutton: +- label = label[1:] +- underline, label = prepstr(label) +- accelerator = get_accelerator(keydefs, eventname) +- def command(text=text, eventname=eventname): +- text.event_generate(eventname) +- if checkbutton: +- var = self.get_var_obj(eventname, BooleanVar) +- menu.add_checkbutton(label=label, underline=underline, +- command=command, accelerator=accelerator, +- variable=var) +- else: +- menu.add_command(label=label, underline=underline, +- command=command, +- accelerator=accelerator) ++ for page in iter_over: ++ text = page.editpage.text ++ text.keydefs = keydefs ++ for event, keylist in keydefs.items(): ++ if keylist: ++ text.event_add(event, *keylist) + + def getvar(self, name): + var = self.get_var_obj(name) +@@ -990,52 +540,25 @@ + else: + raise NameError, name + +- def get_var_obj(self, name, vartype=None): ++ def get_var_obj(self, name, vartype=None, text=None): + var = self.tkinter_vars.get(name) + if not var and vartype: + # create a Tkinter variable object with self.text as master: +- self.tkinter_vars[name] = var = vartype(self.text) ++ self.tkinter_vars[name] = var = vartype(text or self.text) + return var + + # Tk implementations of "virtual text methods" -- each platform + # reusing IDLE's support code needs to define these for its GUI's + # flavor of widget. + +- # Is character at text_index in a Python string? Return 0 for +- # "guaranteed no", true for anything else. This info is expensive +- # to compute ab initio, but is probably already known by the +- # platform's colorizer. +- +- def is_char_in_string(self, text_index): +- if self.color: +- # Return true iff colorizer hasn't (re)gotten this far +- # yet, or the character is tagged as being in a string +- return self.text.tag_prevrange("TODO", text_index) or \ +- "STRING" in self.text.tag_names(text_index) +- else: +- # The colorizer is missing: assume the worst +- return 1 +- +- # If a selection is defined in the text widget, return (start, +- # end) as Tkinter text indices, otherwise return (None, None) +- def get_selection_indices(self): +- try: +- first = self.text.index("sel.first") +- last = self.text.index("sel.last") +- return first, last +- except TclError: +- return None, None +- + # Return the text widget's current view of what a tab stop means + # (equivalent width in spaces). +- +- def get_tabwidth(self): ++ def get_tabwidth(self): # XXX depends on self.text + current = self.text['tabs'] or TK_TABWIDTH_DEFAULT + return int(current) + + # Set the text widget's current view of what a tab stop means. +- +- def set_tabwidth(self, newtabwidth): ++ def set_tabwidth(self, newtabwidth): # XXX depends on self.text + text = self.text + if self.get_tabwidth() != newtabwidth: + pixels = text.tk.call("font", "measure", text["font"], +@@ -1048,7 +571,6 @@ + # indentwidth != tabwidth set usetabs false. + # In any case, adjust the Text widget's view of what a tab + # character means. +- + def set_indentation_params(self, ispythonsource, guess=True): + if guess and ispythonsource: + i = self.guess_indent() +@@ -1058,386 +580,187 @@ + self.usetabs = False + self.set_tabwidth(self.tabwidth) + +- def smart_backspace_event(self, event): +- text = self.text +- first, last = self.get_selection_indices() +- if first and last: +- text.delete(first, last) +- text.mark_set("insert", first) +- return "break" +- # Delete whitespace left, until hitting a real char or closest +- # preceding virtual tab stop. +- chars = text.get("insert linestart", "insert") +- if chars == '': +- if text.compare("insert", ">", "1.0"): +- # easy: delete preceding newline +- text.delete("insert-1c") +- else: +- text.bell() # at start of buffer +- return "break" +- if chars[-1] not in " \t": +- # easy: delete preceding real char +- text.delete("insert-1c") +- return "break" +- # Ick. It may require *inserting* spaces if we back up over a +- # tab character! This is written to be clear, not fast. +- tabwidth = self.tabwidth +- have = len(chars.expandtabs(tabwidth)) +- assert have > 0 +- want = ((have - 1) // self.indentwidth) * self.indentwidth +- # Debug prompt is multilined.... +- last_line_of_prompt = sys.ps1.split('\n')[-1] +- ncharsdeleted = 0 +- while 1: +- if chars == last_line_of_prompt: +- break +- chars = chars[:-1] +- ncharsdeleted = ncharsdeleted + 1 +- have = len(chars.expandtabs(tabwidth)) +- if have <= want or chars[-1] not in " \t": +- break +- text.undo_block_start() +- text.delete("insert-%dc" % ncharsdeleted, "insert") +- if have < want: +- text.insert("insert", ' ' * (want - have)) +- text.undo_block_stop() +- return "break" ++ # Guess indentwidth from text content. ++ # Return guessed indentwidth. This should not be believed unless ++ # it's in a reasonable range (e.g., it will be 0 if no indented ++ # blocks are found). ++ def guess_indent(self): # XXX depends on self.text ++ opener, indented = IndentSearcher(self.text, self.tabwidth).run() ++ if opener and indented: ++ raw, indentsmall = classifyws(opener, self.tabwidth) ++ raw, indentlarge = classifyws(indented, self.tabwidth) ++ else: ++ indentsmall = indentlarge = 0 ++ return indentlarge - indentsmall + +- def smart_indent_event(self, event): +- # if intraline selection: +- # delete it +- # elif multiline selection: +- # do indent-region +- # else: +- # indent one level +- text = self.text +- first, last = self.get_selection_indices() +- text.undo_block_start() +- try: +- if first and last: +- if index2line(first) != index2line(last): +- return self.indent_region_event(event) +- text.delete(first, last) +- text.mark_set("insert", first) +- prefix = text.get("insert linestart", "insert") +- raw, effective = classifyws(prefix, self.tabwidth) +- if raw == len(prefix): +- # only whitespace to the left +- self.reindent_to(effective + self.indentwidth) +- else: +- # tab to the next 'stop' within or to right of line's text: +- if self.usetabs: +- pad = '\t' +- else: +- effective = len(prefix.expandtabs(self.tabwidth)) +- n = self.indentwidth +- pad = ' ' * (n - effective % n) +- text.insert("insert", pad) +- text.see("insert") +- return "break" +- finally: +- text.undo_block_stop() ++ # Private methods/attributes + +- def newline_and_indent_event(self, event): +- text = self.text +- first, last = self.get_selection_indices() +- text.undo_block_start() +- try: +- if first and last: +- text.delete(first, last) +- text.mark_set("insert", first) +- line = text.get("insert linestart", "insert") +- i, n = 0, len(line) +- while i < n and line[i] in " \t": +- i = i+1 +- if i == n: +- # the cursor is in or at leading indentation in a continuation +- # line; just inject an empty line at the start +- text.insert("insert linestart", '\n') +- return "break" +- indent = line[:i] +- # strip whitespace before insert point unless it's in the prompt +- i = 0 +- last_line_of_prompt = sys.ps1.split('\n')[-1] +- while line and line[-1] in " \t" and line != last_line_of_prompt: +- line = line[:-1] +- i = i+1 +- if i: +- text.delete("insert - %d chars" % i, "insert") +- # strip whitespace after insert point +- while text.get("insert") in " \t": +- text.delete("insert") +- # start new line +- text.insert("insert", '\n') ++ # extensions won't have more than one instance per window ++ _unique_extensions = ['CodeContext', 'ScriptBinding', 'FormatParagraph'] + +- # adjust indentation for continuations and block +- # open/close first need to find the last stmt +- lno = index2line(text.index('insert')) +- y = PyParse.Parser(self.indentwidth, self.tabwidth) +- if not self.context_use_ps1: +- for context in self.num_context_lines: +- startat = max(lno - context, 1) +- startatindex = `startat` + ".0" +- rawtext = text.get(startatindex, "insert") +- y.set_str(rawtext) +- bod = y.find_good_parse_start( +- self.context_use_ps1, +- self._build_char_in_string_func(startatindex)) +- if bod is not None or startat == 1: +- break +- y.set_lo(bod or 0) +- else: +- r = text.tag_prevrange("console", "insert") +- if r: +- startatindex = r[1] +- else: +- startatindex = "1.0" +- rawtext = text.get(startatindex, "insert") +- y.set_str(rawtext) +- y.set_lo(0) ++ def _unload_extensions(self): ++ for ins in self.extensions.values(): ++ if hasattr(ins, "close"): ++ ins.close() ++ self.extensions = {} + +- c = y.get_continuation_type() +- if c != PyParse.C_NONE: +- # The current stmt hasn't ended yet. +- if c == PyParse.C_STRING_FIRST_LINE: +- # after the first line of a string; do not indent at all +- pass +- elif c == PyParse.C_STRING_NEXT_LINES: +- # inside a string which started before this line; +- # just mimic the current indent +- text.insert("insert", indent) +- elif c == PyParse.C_BRACKET: +- # line up with the first (if any) element of the +- # last open bracket structure; else indent one +- # level beyond the indent of the line with the +- # last open bracket +- self.reindent_to(y.compute_bracket_indent()) +- elif c == PyParse.C_BACKSLASH: +- # if more than one line in this stmt already, just +- # mimic the current indent; else if initial line +- # has a start on an assignment stmt, indent to +- # beyond leftmost =; else to beyond first chunk of +- # non-whitespace on initial line +- if y.get_num_lines_in_stmt() > 1: +- text.insert("insert", indent) +- else: +- self.reindent_to(y.compute_backslash_indent()) +- else: +- assert 0, "bogus continuation type %r" % (c,) +- return "break" ++ def _load_extension(self, name, tab): ++ ext_loaded = self.extensions.get(name, None) + +- # This line starts a brand new stmt; indent relative to +- # indentation of initial line of closest preceding +- # interesting stmt. +- indent = y.get_base_indent_string() +- text.insert("insert", indent) +- if y.is_block_opener(): +- self.smart_indent_event(event) +- elif indent and y.is_block_closer(): +- self.smart_backspace_event(event) +- return "break" +- finally: +- text.see("insert") +- text.undo_block_stop() ++ try: ++ mod = __import__(name, globals(), locals(), []) ++ except ImportError: ++ print "\nFailed to import extension: ", name ++ return + +- # Our editwin provides a is_char_in_string function that works +- # with a Tk text index, but PyParse only knows about offsets into +- # a string. This builds a function for PyParse that accepts an +- # offset. ++ keydefs = idleConf.GetExtensionBindings(name) + +- def _build_char_in_string_func(self, startindex): +- def inner(offset, _startindex=startindex, +- _icis=self.is_char_in_string): +- return _icis(_startindex + "+%dc" % offset) +- return inner ++ if name not in self._unique_extensions or not ext_loaded: ++ # create a new instance ++ cls = getattr(mod, name) ++ ins = cls(tab.editpage) ++ self.extensions.setdefault(name, []).append(ins) ++ if not ext_loaded: ++ # create new items in menu only if this is the first time this ++ # extension is being loaded in this window ++ if hasattr(cls, "menudefs"): ++ self._fill_menus(cls.menudefs, keydefs) ++ elif name in self._unique_extensions and ext_loaded: ++ # get an existing instance ++ ins = self.extensions[name][0] + +- def indent_region_event(self, event): +- head, tail, chars, lines = self.get_region() +- for pos in range(len(lines)): +- line = lines[pos] +- if line: +- raw, effective = classifyws(line, self.tabwidth) +- effective = effective + self.indentwidth +- lines[pos] = self._make_blanks(effective) + line[raw:] +- self.set_region(head, tail, chars, lines) +- return "break" ++ if keydefs: ++ self.apply_bindings(keydefs, tab) ++ for vevent in keydefs.keys(): ++ methodname = vevent.replace("-", "_") ++ while methodname[:1] == '<': ++ methodname = methodname[1:] ++ while methodname[-1:] == '>': ++ methodname = methodname[:-1] ++ methodname = methodname + "_event" ++ if hasattr(ins, methodname): ++ tab.editpage.text.bind(vevent, getattr(ins, methodname)) + +- def dedent_region_event(self, event): +- head, tail, chars, lines = self.get_region() +- for pos in range(len(lines)): +- line = lines[pos] +- if line: +- raw, effective = classifyws(line, self.tabwidth) +- effective = max(effective - self.indentwidth, 0) +- lines[pos] = self._make_blanks(effective) + line[raw:] +- self.set_region(head, tail, chars, lines) +- return "break" ++ def _load_extensions(self): ++ self._load_standard_extensions(self.text_notebook.last_page()) + +- def comment_region_event(self, event): +- head, tail, chars, lines = self.get_region() +- for pos in range(len(lines) - 1): +- line = lines[pos] +- lines[pos] = '##' + line +- self.set_region(head, tail, chars, lines) ++ def _load_standard_extensions(self, tab): ++ for name in self._get_standard_extension_names(): ++ try: ++ self._load_extension(name, tab) ++ except: ++ print "Failed to load extension", repr(name) ++ traceback.print_exc() + +- def uncomment_region_event(self, event): +- head, tail, chars, lines = self.get_region() +- for pos in range(len(lines)): +- line = lines[pos] +- if not line: +- continue +- if line[:2] == '##': +- line = line[2:] +- elif line[:1] == '#': +- line = line[1:] +- lines[pos] = line +- self.set_region(head, tail, chars, lines) ++ def _get_standard_extension_names(self): ++ return idleConf.GetExtensions(editor_only=True) + +- def tabify_region_event(self, event): +- head, tail, chars, lines = self.get_region() +- tabwidth = self._asktabwidth() +- for pos in range(len(lines)): +- line = lines[pos] +- if line: +- raw, effective = classifyws(line, tabwidth) +- ntabs, nspaces = divmod(effective, tabwidth) +- lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] +- self.set_region(head, tail, chars, lines) ++ def _post_tab_close(self, event): ++ if not self.current_page: ++ # no tabs now, close window ++ self._close() ++ return + +- def untabify_region_event(self, event): +- head, tail, chars, lines = self.get_region() +- tabwidth = self._asktabwidth() +- for pos in range(len(lines)): +- lines[pos] = lines[pos].expandtabs(tabwidth) +- self.set_region(head, tail, chars, lines) ++ def _update_controls(self, event): ++ curr_page = self.current_page ++ if not curr_page: ++ return + +- def toggle_tabs_event(self, event): +- if self.askyesno( +- "Toggle tabs", +- "Turn tabs " + ("on", "off")[self.usetabs] + +- "?\nIndent width " + +- ("will be", "remains at")[self.usetabs] + " 8." + +- "\n Note: a tab is always 8 columns", +- parent=self.text): +- self.usetabs = not self.usetabs +- # Try to prevent inconsistent indentation. +- # User must change indent width manually after using tabs. +- self.indentwidth = 8 +- return "break" ++ self.text = curr_page.text ++ curr_page.saved_change_hook(True, False) # update window title ++ curr_page.text.focus_set() ++ self.set_line_and_column() + +- # XXX this isn't bound to anything -- see tabwidth comments +-## def change_tabwidth_event(self, event): +-## new = self._asktabwidth() +-## if new != self.tabwidth: +-## self.tabwidth = new +-## self.set_indentation_params(0, guess=0) +-## return "break" ++ # update references in extensions that are loaded only once ++ for ext in self._unique_extensions: ++ if ext not in self.extensions: ++ continue ++ ext = self.extensions[ext][0] ++ ext.editpage = curr_page + +- def change_indentwidth_event(self, event): +- new = self.askinteger( +- "Indent width", +- "New indent width (2-16)\n(Always use 8 when using tabs)", +- parent=self.text, +- initialvalue=self.indentwidth, +- minvalue=2, +- maxvalue=16) +- if new and new != self.indentwidth and not self.usetabs: +- self.indentwidth = new +- return "break" ++ def _create_statusbar(self): ++ self.status_bar = MultiStatusBar(self.top) ++ if macosxSupport.runningAsOSXApp(): ++ # Insert some padding to avoid obscuring some of the statusbar ++ # by the resize widget. ++ self.status_bar.set_label('_padding1', ' ', side=RIGHT) ++ self.status_bar.set_label('column', 'Col: ?', side=RIGHT) ++ self.status_bar.set_label('line', 'Ln: ?', side=RIGHT) ++ self.status_bar.pack(side=BOTTOM, fill=X) + +- def get_region(self): +- text = self.text +- first, last = self.get_selection_indices() +- if first and last: +- head = text.index(first + " linestart") +- tail = text.index(last + "-1c lineend +1c") +- else: +- head = text.index("insert linestart") +- tail = text.index("insert lineend +1c") +- chars = text.get(head, tail) +- lines = chars.split("\n") +- return head, tail, chars, lines ++ def _createmenubar(self, text): ++ mbar = self.menubar ++ menudict = self.menudict ++ for name, label in self.menu_specs: ++ underline, label = prepstr(label) ++ menudict[name] = menu = Menu(mbar, name=name, tearoff=0) ++ mbar.add_cascade(label=label, menu=menu, underline=underline) + +- def set_region(self, head, tail, chars, lines): +- text = self.text +- newchars = "\n".join(lines) +- if newchars == chars: +- text.bell() +- return +- text.tag_remove("sel", "1.0", "end") +- text.mark_set("insert", head) +- text.undo_block_start() +- text.delete(head, tail) +- text.insert(head, newchars) +- text.undo_block_stop() +- text.tag_add("sel", head, "insert") ++ if sys.platform == 'darwin' and '.framework' in sys.executable: ++ # Insert the application menu ++ menudict['application'] = menu = Menu(mbar, name='apple') ++ mbar.add_cascade(label='IDLE', menu=menu) + +- # Make string that displays as n leading blanks. ++ self._fill_menus(text=text) ++ self.base_helpmenu_length = self.menudict['help'].index(END) ++ self.reset_help_menu_entries() + +- def _make_blanks(self, n): +- if self.usetabs: +- ntabs, nspaces = divmod(n, self.tabwidth) +- return '\t' * ntabs + ' ' * nspaces +- else: +- return ' ' * n ++ def _fill_menus(self, menudefs=None, keydefs=None, text=None): ++ """Add appropriate entries to the menus and submenus + +- # Delete from beginning of line to insert point, then reinsert +- # column logical (meaning use tabs if appropriate) spaces. ++ Menus that are absent or None in self.menudict are ignored. ++ """ ++ if menudefs is None: ++ menudefs = Bindings.menudefs ++ if keydefs is None: ++ keydefs = Bindings.default_keydefs ++ menudict = self.menudict ++ for mname, entrylist in menudefs: ++ menu = menudict.get(mname) ++ if not menu: ++ continue ++ for entry in entrylist: ++ if not entry: ++ menu.add_separator() ++ else: ++ label, eventname = entry ++ checkbutton = (label[:1] == '!') ++ if checkbutton: ++ label = label[1:] ++ underline, label = prepstr(label) ++ accelerator = get_accelerator(keydefs, eventname) ++ def command(eventname=eventname): ++ self.text.event_generate(eventname) ++ if checkbutton: ++ var = self.get_var_obj(eventname, BooleanVar, text) ++ menu.add_checkbutton(label=label, underline=underline, ++ command=command, accelerator=accelerator, ++ variable=var) ++ else: ++ menu.add_command(label=label, underline=underline, ++ command=command, accelerator=accelerator) + +- def reindent_to(self, column): +- text = self.text +- text.undo_block_start() +- if text.compare("insert linestart", "!=", "insert"): +- text.delete("insert linestart", "insert") +- if column: +- text.insert("insert", self._make_blanks(column)) +- text.undo_block_stop() ++ def __recent_file_callback(self, file_name): ++ def open_recent_file(fn_closure=file_name): ++ self.current_page.io.open(editFile=fn_closure) ++ return open_recent_file + +- def _asktabwidth(self): +- return self.askinteger( +- "Tab width", +- "Columns per tab? (2-16)", +- parent=self.text, +- initialvalue=self.indentwidth, +- minvalue=2, +- maxvalue=16) or self.tabwidth ++ def __extra_help_callback(self, helpfile): ++ "Create a callback with the helpfile value frozen at definition time" ++ def display_extra_help(helpfile=helpfile): ++ if not helpfile.startswith(('www', 'http')): ++ url = os.path.normpath(helpfile) ++ if sys.platform[:3] == 'win': ++ os.startfile(helpfile) ++ else: ++ webbrowser.open(helpfile) ++ return display_extra_help + +- # Guess indentwidth from text content. +- # Return guessed indentwidth. This should not be believed unless +- # it's in a reasonable range (e.g., it will be 0 if no indented +- # blocks are found). +- +- def guess_indent(self): +- opener, indented = IndentSearcher(self.text, self.tabwidth).run() +- if opener and indented: +- raw, indentsmall = classifyws(opener, self.tabwidth) +- raw, indentlarge = classifyws(indented, self.tabwidth) +- else: +- indentsmall = indentlarge = 0 +- return indentlarge - indentsmall +- +-# "line.col" -> line, as an int +-def index2line(index): +- return int(float(index)) +- + # Look at the leading whitespace in s. + # Return pair (# of leading ws characters, + # effective # of leading blanks after expanding + # tabs to width tabwidth) + +-def classifyws(s, tabwidth): +- raw = effective = 0 +- for ch in s: +- if ch == ' ': +- raw = raw + 1 +- effective = effective + 1 +- elif ch == '\t': +- raw = raw + 1 +- effective = (effective // tabwidth + 1) * tabwidth +- else: +- break +- return raw, effective +- + import tokenize + _tokenize = tokenize + del tokenize +@@ -1534,6 +857,7 @@ + + + def test(): ++ from Tkinter import Tk + root = Tk() + fixwordbreaks(root) + root.withdraw() +Index: help.txt +=================================================================== +--- help.txt (revision 63995) ++++ help.txt (revision 65541) +@@ -6,6 +6,7 @@ + File Menu: + + New Window -- Create a new editing window ++ New Tab -- Create a new editing tab + Open... -- Open an existing file + Recent Files... -- Open a list of recent files + Open Module... -- Open an existing module (searches sys.path) +@@ -23,6 +24,7 @@ + --- + Print Window -- Print the current window + --- ++ Close Tab -- Close current tab (asks to save if unsaved) + Close -- Close current window (asks to save if unsaved) + Exit -- Close all windows, quit (asks to save if unsaved) + +Index: editorpage.py +=================================================================== +--- editorpage.py (revision 0) ++++ editorpage.py (revision 65541) +@@ -0,0 +1,925 @@ ++import os ++import sys ++import imp ++import webbrowser ++import tkMessageBox ++import tkSimpleDialog ++from Tkinter import Text, Menu, TclError ++ ++import utils ++import textView ++import aboutDialog ++import configDialog ++import macosxSupport ++import PyParse ++import IOBinding ++import GrepDialog ++import PathBrowser ++import ClassBrowser ++import SearchDialog ++import ReplaceDialog ++from configHandler import idleConf ++from MultiCall import MultiCallCreator ++from Percolator import Percolator ++ ++def classifyws(s, tabwidth): ++ raw = effective = 0 ++ for ch in s: ++ if ch == ' ': ++ raw = raw + 1 ++ effective = effective + 1 ++ elif ch == '\t': ++ raw = raw + 1 ++ effective = (effective // tabwidth + 1) * tabwidth ++ else: ++ break ++ return raw, effective ++ ++def index2line(index): ++ """"line.col" -> line, as an int""" ++ return int(float(index)) ++ ++def filename_to_unicode(filename): ++ """Convert filename to unicode in order to display it in Tk""" ++ if isinstance(filename, unicode) or not filename: ++ return filename ++ else: ++ try: ++ return filename.decode(IOBinding.filesystemencoding) ++ except UnicodeDecodeError: ++ # XXX ++ try: ++ return filename.decode(IOBinding.encoding) ++ except UnicodeDecodeError: ++ # byte-to-byte conversion ++ return filename.decode('iso8859-1') ++ ++def _find_module(fullname, path=None): ++ """Version of imp.find_module() that handles hierarchical module names""" ++ file = None ++ ++ for tgt in fullname.split('.'): ++ if file is not None: ++ file.close() # close intermediate files ++ (file, filename, descr) = imp.find_module(tgt, path) ++ if descr[2] == imp.PY_SOURCE: ++ break # find but not load the source file ++ module = imp.load_module(tgt, file, filename, descr) ++ try: ++ path = module.__path__ ++ except AttributeError: ++ raise ImportError('No source for module %s' % module.__name__) ++ ++ return file, filename, descr ++ ++class EditorPage(object): ++ def __init__(self, parent_frame, editwin, title=None, **kwargs): ++ self.editwin = editwin ++ self.title = title ++ self.tab_initialized = False ++ kwargs.setdefault('width', idleConf.GetOption('main', 'EditorPage', ++ 'width')) ++ kwargs.setdefault('height', idleConf.GetOption('main', 'EditorPage', ++ 'height')) ++ ++ self.text = MultiCallCreator(Text)(parent_frame, **kwargs) ++ self.color = None # initialized in reset_colorizer ++ self.per = Percolator(self.text) ++ self.undo = self.editwin.UndoDelegator() ++ self.per.insertfilter(self.undo) ++ self.text.undo_block_start = self.undo.undo_block_start ++ self.text.undo_block_stop = self.undo.undo_block_stop ++ self.io = IOBinding.IOBinding(self) ++ ++ self.undo.set_saved_change_hook(self.saved_change_hook) ++ self.io.set_filename_change_hook(self.filename_change_hook) ++ self.reset_colorizer() ++ self._setup_bindings() ++ ++ def post_init(self, filename=None, update_window_title=False): ++ if filename: ++ if os.path.exists(filename) and not os.path.isdir(filename): ++ self.io.loadfile(filename) ++ else: ++ self.io.set_filename(filename) ++ self.saved_change_hook(update_window_title=update_window_title) ++ self.tab_initialized = True ++ ++ def close_tab(self, event=None): ++ """Close current tab, if no more tabs present, close the window.""" ++ if hasattr(self.editwin, 'interp'): ++ # this is a PyShell, don't ask to save ++ reply = 'yes' ++ else: ++ reply = str(self.maybesave()) ++ if reply != "cancel": ++ if self.io.filename: ++ self.editwin.update_recent_files_list(new_file=self.io.filename) ++ self.close() ++ self.editwin.text_notebook.remove_page(self.title) ++ self.editwin.top.event_generate('<>') ++ ++ return reply ++ ++ def close(self): ++ """Perform necessary cleanup for this page before closing it.""" ++ self.io.close() ++ self.io = None ++ ++ self.undo = None ++ ++ if self.color: ++ self.color.close(False) ++ self.color = None ++ ++ self.per.close() ++ self.per = None ++ self.text = None ++ ++ # XXX (1) mark where these functions are used ++ def saved_change_hook(self,update_window_title=False,update_tab_title=True): ++ short = self.editwin.short_title() ++ long = self.long_title() ++ ++ if short and long: ++ title = short + " - " + long ++ tabtitle = os.path.split(long)[-1] ++ elif short: ++ title = short ++ tabtitle = short ++ elif long: ++ title = long ++ tabtitle = os.path.split(long)[-1] ++ else: ++ title = tabtitle = "Untitled" ++ icon = short or long or title ++ if not self.get_saved(): ++ title = "*%s*" % title ++ tabtitle = "*%s*" % tabtitle ++ icon = "*%s" % icon ++ ++ if update_tab_title: ++ self.editwin.text_notebook.update_tabtitle(self, tabtitle) ++ if update_window_title or ( ++ update_window_title is None and self.tab_initialized): ++ self.editwin.top.wm_title(title) ++ self.editwin.top.wm_iconname(icon) ++ ++ def get_saved(self): ++ return self.undo.get_saved() ++ ++ def set_saved(self, flag): ++ self.undo.set_saved(flag) ++ ++ def filename_change_hook(self): ++ if self.editwin.flist: ++ self.editwin.flist.filename_changed_edit(self, self.editwin) ++ self.saved_change_hook(self.tab_initialized) ++ self.editwin.top.update_windowlist_registry(self.editwin) ++ self.reset_colorizer() ++ ++ def reset_undo(self): ++ self.undo.reset_undo() ++ ++ def reset_colorizer(self): ++ "Update the colour theme" ++ # Called from self.filename_change_hook and from configDialog.py ++ self.__rmcolorizer() ++ self.__addcolorizer() ++ theme = idleConf.GetOption('main','Theme','name') ++ normal_colors = idleConf.GetHighlight(theme, 'normal') ++ cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg') ++ select_colors = idleConf.GetHighlight(theme, 'hilite') ++ ++ self.text.config( ++ foreground=normal_colors['foreground'], ++ background=normal_colors['background'], ++ insertbackground=cursor_color, ++ selectforeground=select_colors['foreground'], ++ selectbackground=select_colors['background']) ++ ++ def short_title(self): ++ filename = self.io.filename ++ if filename: ++ filename = os.path.basename(filename) ++ # return unicode string to display non-ASCII chars correctly ++ return filename_to_unicode(filename) ++ ++ def long_title(self): ++ # return unicode string to display non-ASCII chars correctly ++ return filename_to_unicode(self.io.filename or "") ++ ++ def maybesave(self): ++ if self.io: ++ if not self.get_saved(): ++ if self.editwin.top.state()!= 'normal': ++ self.editiwn.top.deiconify() ++ self.editwin.top.lower() ++ self.editwin.top.lift() ++ return self.io.maybesave() ++ # XXX (1) end ++ ++ def center(self, mark="insert"): ++ # Used by EditorWindow.gotoline ++ text = self.text ++ top, bot = self._getwindowlines() ++ lineno = self._getlineno(mark) ++ height = bot - top ++ newtop = max(1, lineno - height//2) ++ text.yview(float(newtop)) ++ ++ def newline_and_indent_event(self, event): ++ # Used by EditorWindow.newline_and_indent_event which is used by PyShell ++ text = self.text ++ first, last = self.get_selection_indices() ++ text.undo_block_start() ++ try: ++ if first and last: ++ text.delete(first, last) ++ text.mark_set("insert", first) ++ line = text.get("insert linestart", "insert") ++ i, n = 0, len(line) ++ while i < n and line[i] in " \t": ++ i = i+1 ++ if i == n: ++ # the cursor is in or at leading indentation in a continuation ++ # line; just inject an empty line at the start ++ text.insert("insert linestart", '\n') ++ return "break" ++ indent = line[:i] ++ # strip whitespace before insert point unless it's in the prompt ++ i = 0 ++ last_line_of_prompt = sys.ps1.split('\n')[-1] ++ while line and line[-1] in " \t" and line != last_line_of_prompt: ++ line = line[:-1] ++ i = i+1 ++ if i: ++ text.delete("insert - %d chars" % i, "insert") ++ # strip whitespace after insert point ++ while text.get("insert") in " \t": ++ text.delete("insert") ++ # start new line ++ text.insert("insert", '\n') ++ ++ # adjust indentation for continuations and block ++ # open/close first need to find the last stmt ++ lno = index2line(text.index('insert')) ++ #print self.editwin.indentwidth, self.editwin.tabwidth ++ y = PyParse.Parser(self.editwin.indentwidth, self.editwin.tabwidth) ++ if not self.editwin.context_use_ps1: ++ for context in self.editwin.num_context_lines: ++ startat = max(lno - context, 1) ++ startatindex = `startat` + ".0" ++ rawtext = text.get(startatindex, "insert") ++ y.set_str(rawtext) ++ bod = y.find_good_parse_start( ++ self.editwin.context_use_ps1, ++ self.build_char_in_string_func(startatindex)) ++ if bod is not None or startat == 1: ++ break ++ y.set_lo(bod or 0) ++ else: ++ r = text.tag_prevrange("console", "insert") ++ if r: ++ startatindex = r[1] ++ else: ++ startatindex = "1.0" ++ rawtext = text.get(startatindex, "insert") ++ y.set_str(rawtext) ++ y.set_lo(0) ++ ++ c = y.get_continuation_type() ++ if c != PyParse.C_NONE: ++ # The current stmt hasn't ended yet. ++ if c == PyParse.C_STRING_FIRST_LINE: ++ # after the first line of a string; do not indent at all ++ pass ++ elif c == PyParse.C_STRING_NEXT_LINES: ++ # inside a string which started before this line; ++ # just mimic the current indent ++ text.insert("insert", indent) ++ elif c == PyParse.C_BRACKET: ++ # line up with the first (if any) element of the ++ # last open bracket structure; else indent one ++ # level beyond the indent of the line with the ++ # last open bracket ++ self.__reindent_to(y.compute_bracket_indent()) ++ elif c == PyParse.C_BACKSLASH: ++ # if more than one line in this stmt already, just ++ # mimic the current indent; else if initial line ++ # has a start on an assignment stmt, indent to ++ # beyond leftmost =; else to beyond first chunk of ++ # non-whitespace on initial line ++ if y.get_num_lines_in_stmt() > 1: ++ text.insert("insert", indent) ++ else: ++ self.__reindent_to(y.compute_backslash_indent()) ++ else: ++ assert 0, "bogus continuation type %r" % (c,) ++ return "break" ++ ++ # This line starts a brand new stmt; indent relative to ++ # indentation of initial line of closest preceding ++ # interesting stmt. ++ indent = y.get_base_indent_string() ++ text.insert("insert", indent) ++ if y.is_block_opener(): ++ self._smart_indent_event(event) ++ elif indent and y.is_block_closer(): ++ self._smart_backspace_event(event) ++ return "break" ++ finally: ++ text.see("insert") ++ text.undo_block_stop() ++ ++ # If a selection is defined in the text widget, return (start, ++ # end) as Tkinter text indices, otherwise return (None, None) ++ def get_selection_indices(self): ++ # Used by EditorWindow.get_selection_indices which is used by ++ # FormatParagraph ++ try: ++ first = self.text.index("sel.first") ++ last = self.text.index("sel.last") ++ return first, last ++ except TclError: ++ return None, None ++ ++ # Our editpage provides a __is_char_in_string function that works ++ # with a Tk text index, but PyParse only knows about offsets into ++ # a string. This builds a function for PyParse that accepts an ++ # offset. ++ def build_char_in_string_func(self, startindex): ++ # Used by EditorWindow.build_char_in_string_func which is used by ++ # HyperParser ++ def inner(offset, _startindex=startindex, ++ _icis=self.__is_char_in_string): ++ return _icis(_startindex + "+%dc" % offset) ++ return inner ++ ++ def _setup_bindings(self): ++ text = self.text ++ def bind_them(to_bind, prefix='_%s'): ++ for tb in to_bind: ++ prefix_size = tb.count('<') ++ method_name = tb[prefix_size:-prefix_size].replace('-', '_') ++ text.bind(tb, getattr(self, prefix % method_name.lower())) ++ ++ actions = ('<>', '<>', ++ '<>', '<>', '<>', ++ '<>', '<>', '<>', '<>', ++ '<>', '<>', '<>', ++ '<>') ++ events = ('<>', '<>', '<>', ++ '<>', '<>', '<>', ++ '<>', '<>', '<>', ++ '<>', '<>', '<>', ++ '<>', '<>', '<>', ++ '<>') ++ parent_actions = ('<>', '<>', '<>') ++ ++ bind_them(actions) ++ bind_them(events, prefix="_%s_event") ++ for action in parent_actions: ++ prefix_size = action.count('<') ++ method_name = action[prefix_size:-prefix_size].replace('-', '_') ++ text.bind(action, getattr(self.editwin, method_name)) ++ ++ text.bind('<>', self.close_tab) ++ text.bind('<>', self.newline_and_indent_event) ++ text.bind("<>", lambda event: "break") ++ text.bind("", self._move_at_edge_if_selection(0)) ++ text.bind("", self._move_at_edge_if_selection(1)) ++ text.bind("<3>", self._right_menu) ++ text.bind('<>', self.editwin.set_line_and_column) ++ text.event_add("<>", ++ "", "") ++ ++ if self.editwin.flist: ++ text.bind("<>", ++ utils.callback(self.editwin.new_callback, self)) ++ text.bind("<>", ++ self.editwin.flist.close_all_callback) ++ text.bind("<>", self._open_class_browser) ++ text.bind("<>", self._open_path_browser) ++ ++ if macosxSupport.runningAsOSXApp(): ++ # Command-W on editorwindows doesn't work without this. ++ text.bind('<>', self.editwin.close_event) ++ ++ def _help(self, event=None): ++ fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), ++ 'help.txt') ++ textView.view_file(self.text, 'Help', fn) ++ ++ def _python_docs(self, event=None): ++ if sys.platform[:3] == 'win': ++ os.startfile(self.editwin.help_url) ++ else: ++ webbrowser.open(self.editwin.help_url) ++ return "break" ++ ++ def _about_idle(self, event=None): ++ aboutDialog.AboutDialog(self.text, 'About IDLE') ++ ++ def _open_class_browser(self, event=None): ++ filename = self.io.filename ++ if not filename: ++ tkMessageBox.showerror("No filename", ++ "This buffer has no associated filename", ++ master=self.text) ++ self.text.focus_set() ++ return None ++ head, tail = os.path.split(filename) ++ base, ext = os.path.splitext(tail) ++ ClassBrowser.ClassBrowser(self.editwin.flist, base, [head]) ++ ++ def _open_path_browser(self, event=None): ++ PathBrowser.PathBrowser(self.editwin.flist) ++ ++ def _open_config_dialog(self, event=None): ++ # When changing colors and saving it, it requires the attribute ++ # instance_dict making necessary to pass self.editwin.top as the ++ # parent ++ configDialog.ConfigDialog(self.editwin.top, 'Settings') ++ ++ def _open_module(self, event=None): ++ try: ++ name = self.text.get("sel.first", "sel.last") ++ except TclError: ++ name = "" ++ else: ++ name = name.strip() ++ ++ name = tkSimpleDialog.askstring("Module", ++ "Enter the name of a Python module\n" ++ "to search on sys.path and open:", ++ parent=self.text, initialvalue=name) ++ ++ if name: ++ name = name.strip() ++ if not name: ++ return ++ # XXX Ought to insert current file's directory in front of path ++ try: ++ (f, file, (suffix, mode, type)) = _find_module(name) ++ except (NameError, ImportError), msg: ++ tkMessageBox.showerror("Import error", str(msg), parent=self.text) ++ return ++ ++ if type != imp.PY_SOURCE: ++ tkMessageBox.showerror("Unsupported type", ++ "%s is not a source module" % name, parent=self.text) ++ return ++ if f: ++ f.close() ++ if self.editwin.flist: ++ if idleConf.GetOption('main', 'EditorWindow', 'file-in-tab', ++ default=1, type='bool'): ++ if hasattr(self.editwin, 'interp'): ++ # PyShell window must open a module in a new window ++ action = None ++ else: ++ action = self.editwin.new_tab ++ else: ++ action = None ++ self.editwin.flist.open(file, action) ++ else: ++ self.io.loadfile(file) ++ ++ def _find_event(self, event): ++ SearchDialog.find(self.text) ++ return "break" ++ ++ def _find_again_event(self, event): ++ SearchDialog.find_again(self.text) ++ return "break" ++ ++ def _find_selection_event(self, event): ++ SearchDialog.find_selection(self.text) ++ return "break" ++ ++ def _find_in_files_event(self, event): ++ # XXX not expected to work correctly for now ++ GrepDialog.grep(self.text, self.editwin.io, self.editwin.flist) ++ return "break" ++ ++ def _replace_event(self, event): ++ ReplaceDialog.replace(self.text) ++ return "break" ++ ++ def _center_insert_event(self, event): ++ self.center() ++ ++ def _getwindowlines(self): ++ text = self.text ++ top = self._getlineno("@0,0") ++ bot = self._getlineno("@0,65535") ++ if top == bot and text.winfo_height() == 1: ++ # Geometry manager hasn't run yet ++ height = int(text['height']) ++ bot = top + height - 1 ++ return top, bot ++ ++ def _getlineno(self, mark="insert"): ++ return int(float(self.text.index(mark))) ++ ++ def _goto_line_event(self, event): ++ text = self.text ++ lineno = tkSimpleDialog.askinteger("Goto", "Go to line number:", ++ parent=text) ++ ++ if lineno is None: ++ return "break" ++ ++ if lineno <= 0: ++ text.bell() ++ return "break" ++ ++ text.mark_set("insert", "%d.0" % lineno) ++ text.see("insert") ++ ++ def _cut(self, event): ++ self.text.event_generate("<>") ++ return "break" ++ ++ def _copy(self, event): ++ if not self.text.tag_ranges("sel"): ++ # There is no selection, so do nothing and maybe interrupt. ++ return ++ ++ self.text.event_generate("<>") ++ return "break" ++ ++ def _paste(self, event): ++ self.text.event_generate("<>") ++ self.text.see("insert") ++ return "break" ++ ++ def _select_all(self, event=None): ++ self.text.tag_add("sel", "1.0", "end-1c") ++ self.text.mark_set("insert", "1.0") ++ self.text.see("insert") ++ return "break" ++ ++ def _remove_selection(self, event=None): ++ self.text.tag_remove("sel", "1.0", "end") ++ self.text.see("insert") ++ ++ def _del_word_left(self, event): ++ self.text.event_generate('') ++ return "break" ++ ++ def _del_word_right(self, event): ++ self.text.event_generate('') ++ return "break" ++ ++ def _move_at_edge_if_selection(self, edge_index): ++ """Cursor move begins at start or end of selection ++ When a left/right cursor key is pressed create and return to Tkinter a ++ function which causes a cursor move from the associated edge of the ++ selection. ++ """ ++ text_index = self.text.index ++ text_mark_set = self.text.mark_set ++ edges_table = ("sel.first+1c", "sel.last-1c") ++ ++ def move_at_edge(event): ++ if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed ++ try: ++ text_index("sel.first") ++ text_mark_set("insert", edges_table[edge_index]) ++ except TclError: ++ pass ++ ++ return move_at_edge ++ ++ def _beginning_of_line(self, event): ++ if (event.state & 12) != 0 and event.keysym == "Home": ++ # state&1==shift, state&4==control, state&8==alt ++ return # ; fall back to class binding ++ ++ text = self.text ++ ++ if text.index("iomark") and \ ++ text.compare("iomark", "<=", "insert lineend") and \ ++ text.compare("insert linestart", "<=", "iomark"): ++ insertpt = int(text.index("iomark").split(".")[1]) ++ else: ++ line = text.get("insert linestart", "insert lineend") ++ for insertpt in xrange(len(line)): ++ if line[insertpt] not in (' ','\t'): ++ break ++ else: ++ insertpt=len(line) ++ ++ lineat = int(text.index("insert").split('.')[1]) ++ ++ if insertpt == lineat: ++ insertpt = 0 ++ ++ dest = "insert linestart+%sc" % str(insertpt) ++ ++ if (event.state & 1) == 0: ++ # shift not pressed ++ text.tag_remove("sel", "1.0", "end") ++ else: ++ if not text.index("sel.first"): ++ text.mark_set("anchor", "insert") ++ ++ first = text.index(dest) ++ last = text.index("anchor") ++ ++ if text.compare(first, ">", last): ++ first, last = last, first ++ ++ text.tag_remove("sel", "1.0", "end") ++ text.tag_add("sel", first, last) ++ ++ text.mark_set("insert", dest) ++ text.see("insert") ++ return "break" ++ ++ def _smart_backspace_event(self, event): ++ text = self.text ++ first, last = self.get_selection_indices() ++ if first and last: ++ text.delete(first, last) ++ text.mark_set("insert", first) ++ return "break" ++ # Delete whitespace left, until hitting a real char or closest ++ # preceding virtual tab stop. ++ chars = text.get("insert linestart", "insert") ++ if chars == '': ++ if text.compare("insert", ">", "1.0"): ++ # easy: delete preceding newline ++ text.delete("insert-1c") ++ else: ++ text.bell() # at start of buffer ++ return "break" ++ if chars[-1] not in " \t": ++ # easy: delete preceding real char ++ text.delete("insert-1c") ++ return "break" ++ # Ick. It may require *inserting* spaces if we back up over a ++ # tab character! This is written to be clear, not fast. ++ tabwidth = self.editwin.tabwidth ++ have = len(chars.expandtabs(tabwidth)) ++ assert have > 0 ++ want = ((have - 1) // ++ self.editwin.indentwidth) * self.editwin.indentwidth ++ # Debug prompt is multilined.... ++ last_line_of_prompt = sys.ps1.split('\n')[-1] ++ ncharsdeleted = 0 ++ while 1: ++ if chars == last_line_of_prompt: ++ break ++ chars = chars[:-1] ++ ncharsdeleted = ncharsdeleted + 1 ++ have = len(chars.expandtabs(tabwidth)) ++ if have <= want or chars[-1] not in " \t": ++ break ++ text.undo_block_start() ++ text.delete("insert-%dc" % ncharsdeleted, "insert") ++ if have < want: ++ text.insert("insert", ' ' * (want - have)) ++ text.undo_block_stop() ++ return "break" ++ ++ def _smart_indent_event(self, event): ++ # if intraline selection: ++ # delete it ++ # elif multiline selection: ++ # do indent-region ++ # else: ++ # indent one level ++ text = self.text ++ first, last = self.get_selection_indices() ++ text.undo_block_start() ++ try: ++ if first and last: ++ if index2line(first) != index2line(last): ++ return self._indent_region_event(event) ++ text.delete(first, last) ++ text.mark_set("insert", first) ++ prefix = text.get("insert linestart", "insert") ++ raw, effective = classifyws(prefix, self.editwin.tabwidth) ++ if raw == len(prefix): ++ # only whitespace to the left ++ self.__reindent_to(effective + self.editwin.indentwidth) ++ else: ++ # tab to the next 'stop' within or to right of line's text: ++ if self.editwin.usetabs: ++ pad = '\t' ++ else: ++ effective = len(prefix.expandtabs(self.editwin.tabwidth)) ++ n = self.editwin.indentwidth ++ pad = ' ' * (n - effective % n) ++ text.insert("insert", pad) ++ text.see("insert") ++ return "break" ++ finally: ++ text.undo_block_stop() ++ ++ def _indent_region_event(self, event): ++ head, tail, chars, lines = self.__get_region() ++ for pos in range(len(lines)): ++ line = lines[pos] ++ if line: ++ raw, effective = classifyws(line, self.editwin.tabwidth) ++ effective = effective + self.editwin.indentwidth ++ lines[pos] = self.__make_blanks(effective) + line[raw:] ++ self.__set_region(head, tail, chars, lines) ++ return "break" ++ ++ def _dedent_region_event(self, event): ++ head, tail, chars, lines = self.__get_region() ++ for pos in range(len(lines)): ++ line = lines[pos] ++ if line: ++ raw, effective = classifyws(line, self.editwin.tabwidth) ++ effective = max(effective - self.editwin.indentwidth, 0) ++ lines[pos] = self.__make_blanks(effective) + line[raw:] ++ self.__set_region(head, tail, chars, lines) ++ return "break" ++ ++ def _comment_region_event(self, event): ++ head, tail, chars, lines = self.__get_region() ++ for pos in range(len(lines) - 1): ++ line = lines[pos] ++ lines[pos] = '##' + line ++ self.__set_region(head, tail, chars, lines) ++ ++ def _uncomment_region_event(self, event): ++ head, tail, chars, lines = self.__get_region() ++ for pos in range(len(lines)): ++ line = lines[pos] ++ if not line: ++ continue ++ if line[:2] == '##': ++ line = line[2:] ++ elif line[:1] == '#': ++ line = line[1:] ++ lines[pos] = line ++ self.__set_region(head, tail, chars, lines) ++ ++ def _tabify_region_event(self, event): ++ head, tail, chars, lines = self.__get_region() ++ tabwidth = self.__asktabwidth() ++ for pos in range(len(lines)): ++ line = lines[pos] ++ if line: ++ raw, effective = classifyws(line, tabwidth) ++ ntabs, nspaces = divmod(effective, tabwidth) ++ lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:] ++ self.__set_region(head, tail, chars, lines) ++ ++ def _untabify_region_event(self, event): ++ head, tail, chars, lines = self.__get_region() ++ tabwidth = self.__asktabwidth() ++ for pos in range(len(lines)): ++ lines[pos] = lines[pos].expandtabs(tabwidth) ++ self.__set_region(head, tail, chars, lines) ++ ++ def _toggle_tabs_event(self, event): ++ if self.editwin.askyesno( ++ "Toggle tabs", ++ "Turn tabs " + ("on", "off")[self.editwin.usetabs] + ++ "?\nIndent width " + ++ ("will be", "remains at")[self.editwin.usetabs] + " 8." + ++ "\n Note: a tab is always 8 columns", ++ parent=self.text): ++ self.editwin.usetabs = not self.editwin.usetabs ++ # Try to prevent inconsistent indentation. ++ # User must change indent width manually after using tabs. ++ self.editwin.indentwidth = 8 ++ return "break" ++ ++ def _change_indentwidth_event(self, event): ++ new = self.editwin.askinteger( ++ "Indent width", ++ "New indent width (2-16)\n(Always use 8 when using tabs)", ++ parent=self.text, ++ initialvalue=self.editwin.indentwidth, ++ minvalue=2, ++ maxvalue=16) ++ if new and new != self.editwin.indentwidth and not self.editwin.usetabs: ++ self.editwin.indentwidth = new ++ return "break" ++ ++ def _right_menu(self, event): ++ self.text.tag_remove("sel", "1.0", "end") ++ self.text.mark_set("insert", "@%d,%d" % (event.x, event.y)) ++ ++ if not self.editwin.rmenu: ++ self.__make_rmenu() ++ ++ rmenu = self.editwin.rmenu ++ self.event = event ++ iswin = sys.platform[:3] == 'win' ++ if iswin: ++ self.text.config(cursor="arrow") ++ rmenu.tk_popup(event.x_root, event.y_root) ++ if iswin: ++ self.text.config(cursor="ibeam") ++ ++ def __make_rmenu(self): ++ rmenu = Menu(self.text, tearoff=False) ++ ++ for label, eventname in self.editwin.rmenu_specs: ++ def command(text=self.text, eventname=eventname): ++ text.event_generate(eventname) ++ rmenu.add_command(label=label, command=command) ++ ++ self.editwin.rmenu = rmenu ++ ++ def __rmcolorizer(self): ++ if not self.color: ++ return ++ self.color.removecolors() ++ self.per.removefilter(self.color) ++ self.color = None ++ ++ def __addcolorizer(self): ++ if self.color: ++ return ++ if self.editwin.ispythonsource(self.io.filename): ++ self.color = self.editwin.ColorDelegator() ++ ++ # can add more colorizers here... ++ if self.color: ++ self.per.removefilter(self.undo) ++ self.per.insertfilter(self.color) ++ self.per.insertfilter(self.undo) ++ ++ def __asktabwidth(self): ++ return self.editwin.askinteger( ++ "Tab width", ++ "Columns per tab? (2-16)", ++ parent=self.text, ++ initialvalue=self.editwin.indentwidth, ++ minvalue=2, ++ maxvalue=16) or self.editwin.tabwidth ++ ++ # Make string that displays as n leading blanks. ++ def __make_blanks(self, n): ++ if self.editwin.usetabs: ++ ntabs, nspaces = divmod(n, self.editwin.tabwidth) ++ return '\t' * ntabs + ' ' * nspaces ++ else: ++ return ' ' * n ++ ++ def __get_region(self): ++ text = self.text ++ first, last = self.get_selection_indices() ++ if first and last: ++ head = text.index(first + " linestart") ++ tail = text.index(last + "-1c lineend +1c") ++ else: ++ head = text.index("insert linestart") ++ tail = text.index("insert lineend +1c") ++ chars = text.get(head, tail) ++ lines = chars.split("\n") ++ return head, tail, chars, lines ++ ++ def __set_region(self, head, tail, chars, lines): ++ text = self.text ++ newchars = "\n".join(lines) ++ if newchars == chars: ++ text.bell() ++ return ++ text.tag_remove("sel", "1.0", "end") ++ text.mark_set("insert", head) ++ text.undo_block_start() ++ text.delete(head, tail) ++ text.insert(head, newchars) ++ text.undo_block_stop() ++ text.tag_add("sel", head, "insert") ++ ++ # Delete from beginning of line to insert point, then reinsert ++ # column logical (meaning use tabs if appropriate) spaces. ++ def __reindent_to(self, column): ++ text = self.text ++ text.undo_block_start() ++ if text.compare("insert linestart", "!=", "insert"): ++ text.delete("insert linestart", "insert") ++ if column: ++ text.insert("insert", self.__make_blanks(column)) ++ text.undo_block_stop() ++ ++ # Tk implementations of "virtual text methods" -- each platform ++ # reusing IDLE's support code needs to define these for its GUI's ++ # flavor of widget. ++ ++ # Is character at text_index in a Python string? Return 0 for ++ # "guaranteed no", true for anything else. This info is expensive ++ # to compute ab initio, but is probably already known by the ++ # platform's colorizer. ++ def __is_char_in_string(self, text_index): ++ if self.color: ++ # Return true iff colorizer hasn't (re)gotten this far ++ # yet, or the character is tagged as being in a string ++ return self.text.tag_prevrange("TODO", text_index) or \ ++ "STRING" in self.text.tag_names(text_index) ++ else: ++ # The colorizer is missing: assume the worst ++ return 1 +Index: OutputWindow.py +=================================================================== +--- OutputWindow.py (revision 63995) ++++ OutputWindow.py (revision 65541) +@@ -1,8 +1,9 @@ +-from Tkinter import * +-from EditorWindow import EditorWindow + import re + import tkMessageBox ++ ++import utils + import IOBinding ++from EditorWindow import EditorWindow + + class OutputWindow(EditorWindow): + +@@ -14,8 +15,15 @@ + + def __init__(self, *args): + EditorWindow.__init__(self, *args) +- self.text.bind("<>", self.goto_file_line) ++ self.top.bind("<>", self.__configure_new_tab) ++ # configure the tab created in EditorWindow.__init__ ++ self.__configure_new_tab() + ++ def __configure_new_tab(self, event=None): ++ page = self.text_notebook.last_page().editpage ++ page.text.bind("<>", utils.callback(self.goto_file_line, ++ page.text)) ++ + # Customize EditorWindow + + def ispythonsource(self, filename): +@@ -34,7 +42,8 @@ + + # Act as output file + +- def write(self, s, tags=(), mark="insert"): ++ def write(self, s, tags=(), mark="insert", text=None): ++ assert text is not None + # Tk assumes that byte strings are Latin-1; + # we assume that they are in the locale's encoding + if isinstance(s, str): +@@ -43,12 +52,12 @@ + except UnicodeError: + # some other encoding; let Tcl deal with it + pass +- self.text.insert(mark, s, tags) +- self.text.see(mark) +- self.text.update() ++ text.insert(mark, s, tags) ++ text.see(mark) ++ text.update() + + def writelines(self, l): +- map(self.write, l) ++ map(self.write, l, text=self.current_page.text) + + def flush(self): + pass +@@ -67,28 +76,26 @@ + + file_line_progs = None + +- def goto_file_line(self, event=None): ++ def goto_file_line(self, event, text): + if self.file_line_progs is None: + l = [] + for pat in self.file_line_pats: + l.append(re.compile(pat, re.IGNORECASE)) + self.file_line_progs = l +- # x, y = self.event.x, self.event.y +- # self.text.mark_set("insert", "@%d,%d" % (x, y)) +- line = self.text.get("insert linestart", "insert lineend") ++ ++ line = text.get("insert linestart", "insert lineend") + result = self._file_line_helper(line) + if not result: + # Try the previous line. This is handy e.g. in tracebacks, + # where you tend to right-click on the displayed source line +- line = self.text.get("insert -1line linestart", +- "insert -1line lineend") ++ line = text.get("insert -1line linestart", "insert -1line ineend") + result = self._file_line_helper(line) + if not result: + tkMessageBox.showerror( + "No special line", + "The line you point at doesn't look like " + "a valid file name followed by a line number.", +- master=self.text) ++ master=text) + return + filename, lineno = result + edit = self.flist.open(filename) +@@ -111,47 +118,3 @@ + return filename, int(lineno) + except TypeError: + return None +- +-# These classes are currently not used but might come in handy +- +-class OnDemandOutputWindow: +- +- tagdefs = { +- # XXX Should use IdlePrefs.ColorPrefs +- "stdout": {"foreground": "blue"}, +- "stderr": {"foreground": "#007700"}, +- } +- +- def __init__(self, flist): +- self.flist = flist +- self.owin = None +- +- def write(self, s, tags, mark): +- if not self.owin: +- self.setup() +- self.owin.write(s, tags, mark) +- +- def setup(self): +- self.owin = owin = OutputWindow(self.flist) +- text = owin.text +- for tag, cnf in self.tagdefs.items(): +- if cnf: +- text.tag_configure(tag, **cnf) +- text.tag_raise('sel') +- self.write = self.owin.write +- +-#class PseudoFile: +-# +-# def __init__(self, owin, tags, mark="end"): +-# self.owin = owin +-# self.tags = tags +-# self.mark = mark +- +-# def write(self, s): +-# self.owin.write(s, self.tags, self.mark) +- +-# def writelines(self, l): +-# map(self.write, l) +- +-# def flush(self): +-# pass +Index: aboutDialog.py +=================================================================== +--- aboutDialog.py (revision 63995) ++++ aboutDialog.py (revision 65541) +@@ -1,13 +1,18 @@ +-"""About Dialog for IDLE ++"""About Dialog for IDLE""" ++import os ++import sys ++from Tkinter import Toplevel, Frame, Button, Label, TkVersion ++from Tkconstants import LEFT, NSEW, SUNKEN, EW, W, BOTH, TOP, BOTTOM + +-""" +- +-from Tkinter import * +-import os +-import os.path ++import idlever + import textView +-import idlever ++from stylist import PoorManStyle ++from configHandler import idleConf + ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Frame, Button, Label, Style ++ + class AboutDialog(Toplevel): + """Modal about dialog for idle + +@@ -15,12 +20,15 @@ + def __init__(self,parent,title): + Toplevel.__init__(self, parent) + self.configure(borderwidth=5) ++ + self.geometry("+%d+%d" % (parent.winfo_rootx()+30, + parent.winfo_rooty()+30)) + self.bg = "#707070" + self.fg = "#ffffff" ++ ++ self.SetupStyles() + self.CreateWidgets() +- self.resizable(height=FALSE, width=FALSE) ++ self.resizable(height=False, width=False) + self.title(title) + self.transient(parent) + self.grab_set() +@@ -31,40 +39,44 @@ + self.bind('',self.Ok) #dismiss dialog + self.wait_window() + ++ def SetupStyles(self): ++ if TTK: ++ style = Style(self.master) ++ style.configure('Color.TLabel', foreground=self.fg, ++ background=self.bg) ++ style.configure('Color.TFrame', background=self.bg) ++ self.ttkstyle = style ++ self.style = lambda w, style: w.configure(style=style) ++ else: ++ self.style = PoorManStyle(self, ++ styles={'Color.TLabel': {'fg': self.fg, 'bg': self.bg}, ++ 'Color.TFrame': {'bg': self.bg}}).style_it ++ + def CreateWidgets(self): + frameMain = Frame(self, borderwidth=2, relief=SUNKEN) + frameButtons = Frame(self) +- frameButtons.pack(side=BOTTOM, fill=X) +- frameMain.pack(side=TOP, expand=TRUE, fill=BOTH) +- self.buttonOk = Button(frameButtons, text='Close', +- command=self.Ok) +- self.buttonOk.pack(padx=5, pady=5) +- #self.picture = Image('photo', data=self.pictureData) +- frameBg = Frame(frameMain, bg=self.bg) +- frameBg.pack(expand=TRUE, fill=BOTH) +- labelTitle = Label(frameBg, text='IDLE', fg=self.fg, bg=self.bg, +- font=('courier', 24, 'bold')) ++ frameButtons.pack(side=BOTTOM, pady=3) ++ frameMain.pack(side=TOP, expand=True, fill=BOTH) ++ self.buttonOk = Button(frameButtons, text='Close', command=self.Ok) ++ self.buttonOk.pack() ++ frameBg = Frame(frameMain) ++ frameBg.pack(expand=True, fill=BOTH) ++ labelTitle = Label(frameBg, text='IDLE', font=('courier', 24, 'bold')) + labelTitle.grid(row=0, column=0, sticky=W, padx=10, pady=10) +- #labelPicture = Label(frameBg, text='[picture]') +- #image=self.picture, bg=self.bg) +- #labelPicture.grid(row=1, column=1, sticky=W, rowspan=2, +- # padx=0, pady=3) + byline = "Python's Integrated DeveLopment Environment" + 5*'\n' +- labelDesc = Label(frameBg, text=byline, justify=LEFT, +- fg=self.fg, bg=self.bg) ++ labelDesc = Label(frameBg, text=byline, justify=LEFT) + labelDesc.grid(row=2, column=0, sticky=W, columnspan=3, padx=10, pady=5) + labelEmail = Label(frameBg, text='email: idle-dev at python.org', +- justify=LEFT, fg=self.fg, bg=self.bg) ++ justify=LEFT) + labelEmail.grid(row=6, column=0, columnspan=2, + sticky=W, padx=10, pady=0) + labelWWW = Label(frameBg, text='www: http://www.python.org/idle/', +- justify=LEFT, fg=self.fg, bg=self.bg) ++ justify=LEFT) + labelWWW.grid(row=7, column=0, columnspan=2, sticky=W, padx=10, pady=0) +- Frame(frameBg, borderwidth=1, relief=SUNKEN, +- height=2, bg=self.bg).grid(row=8, column=0, sticky=EW, +- columnspan=3, padx=5, pady=5) ++ fbg = Frame(frameBg, borderwidth=1, relief=SUNKEN, height=2) ++ fbg.grid(row=8, column=0, sticky=EW, columnspan=3, padx=5, pady=5) + labelPythonVer = Label(frameBg, text='Python version: ' + \ +- sys.version.split()[0], fg=self.fg, bg=self.bg) ++ sys.version.split()[0]) + labelPythonVer.grid(row=9, column=0, sticky=W, padx=10, pady=0) + # handle weird tk version num in windoze python >= 1.6 (?!?) + tkVer = repr(TkVersion).split('.') +@@ -72,44 +84,50 @@ + if tkVer[len(tkVer)-1] == '': + tkVer[len(tkVer)-1] = '0' + tkVer = '.'.join(tkVer) +- labelTkVer = Label(frameBg, text='Tk version: '+ +- tkVer, fg=self.fg, bg=self.bg) ++ labelTkVer = Label(frameBg, text='Tk version: '+ tkVer) + labelTkVer.grid(row=9, column=1, sticky=W, padx=2, pady=0) +- py_button_f = Frame(frameBg, bg=self.bg) ++ py_button_f = Frame(frameBg) + py_button_f.grid(row=10, column=0, columnspan=2, sticky=NSEW) + buttonLicense = Button(py_button_f, text='License', width=8, +- highlightbackground=self.bg, + command=self.ShowLicense) + buttonLicense.pack(side=LEFT, padx=10, pady=10) + buttonCopyright = Button(py_button_f, text='Copyright', width=8, +- highlightbackground=self.bg, + command=self.ShowCopyright) + buttonCopyright.pack(side=LEFT, padx=10, pady=10) + buttonCredits = Button(py_button_f, text='Credits', width=8, +- highlightbackground=self.bg, + command=self.ShowPythonCredits) + buttonCredits.pack(side=LEFT, padx=10, pady=10) +- Frame(frameBg, borderwidth=1, relief=SUNKEN, +- height=2, bg=self.bg).grid(row=11, column=0, sticky=EW, +- columnspan=3, padx=5, pady=5) +- idle_v = Label(frameBg, text='IDLE version: ' + idlever.IDLE_VERSION, +- fg=self.fg, bg=self.bg) ++ fbg2 = Frame(frameBg, borderwidth=1, relief=SUNKEN, height=2) ++ fbg2.grid(row=11, column=0, sticky=EW, columnspan=3, padx=5, pady=5) ++ idle_v = Label(frameBg, text='IDLE version: ' + idlever.IDLE_VERSION) + idle_v.grid(row=12, column=0, sticky=W, padx=10, pady=0) +- idle_button_f = Frame(frameBg, bg=self.bg) ++ idle_button_f = Frame(frameBg) + idle_button_f.grid(row=13, column=0, columnspan=3, sticky=NSEW) + idle_about_b = Button(idle_button_f, text='README', width=8, +- highlightbackground=self.bg, + command=self.ShowIDLEAbout) + idle_about_b.pack(side=LEFT, padx=10, pady=10) + idle_news_b = Button(idle_button_f, text='NEWS', width=8, +- highlightbackground=self.bg, + command=self.ShowIDLENEWS) + idle_news_b.pack(side=LEFT, padx=10, pady=10) + idle_credits_b = Button(idle_button_f, text='Credits', width=8, +- highlightbackground=self.bg, + command=self.ShowIDLECredits) + idle_credits_b.pack(side=LEFT, padx=10, pady=10) + ++ s = self.style ++ s(frameButtons, 'RootColor.TFrame') ++ s(frameBg, 'Color.TFrame') ++ s(labelTitle, 'Color.TLabel') ++ s(labelDesc, 'Color.TLabel') ++ s(labelEmail, 'Color.TLabel') ++ s(labelWWW, 'Color.TLabel') ++ s(fbg, 'Color.TFrame') ++ s(labelPythonVer, 'Color.TLabel') ++ s(labelTkVer, 'Color.TLabel') ++ s(py_button_f, 'Color.TFrame') ++ s(fbg2, 'Color.TFrame') ++ s(idle_v, 'Color.TLabel') ++ s(idle_button_f, 'Color.TFrame') ++ + def ShowLicense(self): + self.display_printer_text('About - License', license) + +@@ -142,9 +160,9 @@ + + if __name__ == '__main__': + # test the dialog ++ from Tkinter import Tk + root = Tk() + def run(): +- import aboutDialog +- aboutDialog.AboutDialog(root, 'About') ++ AboutDialog(root, 'About') + Button(root, text='Dialog', command=run).pack() + root.mainloop() +Index: config-main.def +=================================================================== +--- config-main.def (revision 63995) ++++ config-main.def (revision 65541) +@@ -49,8 +49,14 @@ + print-command-posix=lpr %s + print-command-win=start /min notepad /p %s + delete-exitfunc= 1 ++use-ttk = 0 + + [EditorWindow] ++width = 80 ++height = 40 ++file-in-tab = 1 ++ ++[EditorPage] + width= 80 + height= 40 + font= courier +@@ -68,6 +74,7 @@ + [Theme] + default= 1 + name= IDLE Classic ++displaytheme = default + + [Keys] + default= 1 +Index: PathBrowser.py +=================================================================== +--- PathBrowser.py (revision 63995) ++++ PathBrowser.py (revision 65541) +@@ -15,25 +15,29 @@ + self.top.wm_iconname("Path Browser") + + def rootnode(self): +- return PathBrowserTreeItem() ++ return PathBrowserTreeItem(self.flist) + + class PathBrowserTreeItem(TreeItem): + ++ def __init__(self, flist): ++ self.flist = flist ++ + def GetText(self): + return "sys.path" + + def GetSubList(self): + sublist = [] + for dir in sys.path: +- item = DirBrowserTreeItem(dir) ++ item = DirBrowserTreeItem(dir, flist=self.flist) + sublist.append(item) + return sublist + + class DirBrowserTreeItem(TreeItem): + +- def __init__(self, dir, packages=[]): ++ def __init__(self, dir, packages=[], flist=None): + self.dir = dir + self.packages = packages ++ self.flist = flist + + def GetText(self): + if not self.packages: +@@ -55,10 +59,11 @@ + packages.sort() + sublist = [] + for nn, name, file in packages: +- item = DirBrowserTreeItem(file, self.packages + [name]) ++ item = DirBrowserTreeItem(file, self.packages + [name], self.flist) + sublist.append(item) + for nn, name in self.listmodules(names): +- item = ModuleBrowserTreeItem(os.path.join(self.dir, name)) ++ item = ModuleBrowserTreeItem(os.path.join(self.dir, name), ++ self.flist) + sublist.append(item) + return sublist + +Index: IOBinding.py +=================================================================== +--- IOBinding.py (revision 63995) ++++ IOBinding.py (revision 65541) +@@ -6,17 +6,20 @@ + # which will only understand the local convention. + + import os ++import re ++import sys + import types +-import sys + import codecs + import tempfile + import tkFileDialog + import tkMessageBox +-import re +-from Tkinter import * ++from Tkinter import Toplevel, Entry, Frame, Button, Label ++from Tkconstants import W, X, TOP, LEFT, BOTH + from SimpleDialog import SimpleDialog + + from configHandler import idleConf ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Entry, Frame, Button, Label + + try: + from codecs import BOM_UTF8 +@@ -93,11 +96,11 @@ + # For some reason, the text is not selectable anymore if the + # widget is disabled. + # l2['state'] = DISABLED +- l2.pack(side=TOP, anchor = W, fill=X) ++ l2.pack(side=TOP, anchor=W, fill=X) + l3 = Label(top, text="to your file\n" + "Choose OK to save this file as %s\n" + "Edit your general options to silence this warning" % enc) +- l3.pack(side=TOP, anchor = W) ++ l3.pack(side=TOP, anchor=W) + + buttons = Frame(top) + buttons.pack(side=TOP, fill=X) +@@ -142,10 +145,14 @@ + + + class IOBinding: ++ filename_change_hook = None ++ filename = None ++ dirname = None + +- def __init__(self, editwin): +- self.editwin = editwin +- self.text = editwin.text ++ def __init__(self, editpage): ++ self.editpage = editpage ++ self.editwin = editpage.editwin ++ self.text = editpage.text + self.__id_open = self.text.bind("<>", self.open) + self.__id_save = self.text.bind("<>", self.save) + self.__id_saveas = self.text.bind("<>", +@@ -163,27 +170,23 @@ + self.text.unbind("<>", self.__id_savecopy) + self.text.unbind("<>", self.__id_print) + # Break cycles ++ self.text = None ++ self.editpage = None + self.editwin = None +- self.text = None + self.filename_change_hook = None + + def get_saved(self): +- return self.editwin.get_saved() ++ return self.editpage.get_saved() + + def set_saved(self, flag): +- self.editwin.set_saved(flag) ++ self.editpage.set_saved(flag) + + def reset_undo(self): +- self.editwin.reset_undo() ++ self.editpage.reset_undo() + +- filename_change_hook = None +- + def set_filename_change_hook(self, hook): + self.filename_change_hook = hook + +- filename = None +- dirname = None +- + def set_filename(self, filename): + if filename and os.path.isdir(filename): + self.filename = None +@@ -200,21 +203,33 @@ + if not editFile: + filename = self.askopenfile() + else: +- filename=editFile ++ filename = editFile + if filename: + # If the current window has no filename and hasn't been +- # modified, we replace its contents (no loss). Otherwise +- # we open a new window. But we won't replace the +- # shell window (which has an interp(reter) attribute), which +- # gets set to "not modified" at every new prompt. ++ # modified, we replace its contents (no loss). Otherwise ++ # we open a new window, or maybe open in a tab. ++ # But we won't replace the shell window (which has an ++ # interp(reter) attribute), which gets set to "not modified" ++ # at every new prompt. + try: + interp = self.editwin.interp + except AttributeError: + interp = None ++ + if not self.filename and self.get_saved() and not interp: + self.editwin.flist.open(filename, self.loadfile) + else: +- self.editwin.flist.open(filename) ++ if idleConf.GetOption('main', 'EditorWindow', ++ 'file-in-tab', default=1, type='bool'): ++ if interp: ++ # this is a PyShell, force file to be opened in a ++ # new window ++ action = None ++ else: ++ action = self.editwin.new_tab ++ else: ++ action = None ++ self.editwin.flist.open(filename, action) + else: + self.text.focus_set() + return "break" +@@ -341,8 +356,8 @@ + if self.writefile(self.filename): + self.set_saved(1) + try: +- self.editwin.store_file_breaks() +- except AttributeError: # may be a PyShell ++ self.editwin.store_file_breaks(self.editpage) ++ except AttributeError: # may not be a PyShell + pass + self.text.focus_set() + return "break" +@@ -354,7 +369,7 @@ + self.set_filename(filename) + self.set_saved(1) + try: +- self.editwin.store_file_breaks() ++ self.editwin.store_file_breaks(self.editpage) + except AttributeError: + pass + self.text.focus_set() +@@ -381,8 +396,7 @@ + f.close() + return True + except IOError, msg: +- tkMessageBox.showerror("I/O Error", str(msg), +- master=self.text) ++ tkMessageBox.showerror("I/O Error", str(msg), master=self.text) + return False + + def encode(self, chars): +@@ -429,8 +443,7 @@ + return BOM_UTF8 + chars.encode("utf-8") + # Nothing was declared, and we had not determined an encoding + # on loading. Recommend an encoding line. +- config_encoding = idleConf.GetOption("main","EditorWindow", +- "encoding") ++ config_encoding = idleConf.GetOption("main", "EditorPage", "encoding") + if config_encoding == 'utf-8': + # User has requested that we save files as UTF-8 + return BOM_UTF8 + chars.encode("utf-8") +@@ -563,6 +576,7 @@ + self.editwin.update_recent_files_list(filename) + + def test(): ++ from Tkinter import Tk, Text + root = Tk() + class MyEditWin: + def __init__(self, text): +Index: WindowList.py +=================================================================== +--- WindowList.py (revision 63995) ++++ WindowList.py (revision 65541) +@@ -1,4 +1,4 @@ +-from Tkinter import * ++from Tkinter import Toplevel, TclError + + class WindowList: + +@@ -45,8 +45,8 @@ + try: + callback() + except: +- print "warning: callback failed in WindowList", \ +- sys.exc_type, ":", sys.exc_value ++ print ("warning: callback failed in WindowList", ++ sys.exc_type, ":", sys.exc_value) + + registry = WindowList() + +Index: ScrolledList.py +=================================================================== +--- ScrolledList.py (revision 63995) ++++ ScrolledList.py (revision 65541) +@@ -1,5 +1,9 @@ +-from Tkinter import * ++from Tkinter import Frame, Menu, Listbox, Scrollbar ++from idlelib.configHandler import idleConf + ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Frame, Scrollbar ++ + class ScrolledList: + + default = "(None)" +@@ -120,6 +124,7 @@ + + + def test(): ++ from Tkinter import Tk + root = Tk() + root.protocol("WM_DELETE_WINDOW", root.destroy) + class MyScrolledList(ScrolledList): +Index: ClassBrowser.py +=================================================================== +--- ClassBrowser.py (revision 63995) ++++ ClassBrowser.py (revision 65541) +@@ -14,7 +14,6 @@ + import sys + import pyclbr + +-import PyShell + from WindowList import ListedToplevel + from TreeWidget import TreeNode, TreeItem, ScrolledCanvas + from configHandler import idleConf +@@ -26,6 +25,7 @@ + # XXX the code here is bogus! + self.name = name + self.file = os.path.join(path[0], self.name + ".py") ++ self.flist = None + self.init(flist) + + def close(self, event=None): +@@ -57,12 +57,13 @@ + self.top.wm_iconname("Class Browser") + + def rootnode(self): +- return ModuleBrowserTreeItem(self.file) ++ return ModuleBrowserTreeItem(self.file, self.flist) + + class ModuleBrowserTreeItem(TreeItem): + +- def __init__(self, file): ++ def __init__(self, file, flist): + self.file = file ++ self.flist = flist + + def GetText(self): + return os.path.basename(self.file) +@@ -73,7 +74,8 @@ + def GetSubList(self): + sublist = [] + for name in self.listclasses(): +- item = ClassBrowserTreeItem(name, self.classes, self.file) ++ item = ClassBrowserTreeItem(name, self.classes, self.file, ++ self.flist) + sublist.append(item) + return sublist + +@@ -82,7 +84,7 @@ + return + if not os.path.exists(self.file): + return +- PyShell.flist.open(self.file) ++ self.flist.open(self.file) + + def IsExpandable(self): + return os.path.normcase(self.file[-3:]) == ".py" +@@ -122,10 +124,11 @@ + + class ClassBrowserTreeItem(TreeItem): + +- def __init__(self, name, classes, file): ++ def __init__(self, name, classes, file, flist): + self.name = name + self.classes = classes + self.file = file ++ self.flist = flist + try: + self.cl = self.classes[self.name] + except (IndexError, KeyError): +@@ -163,7 +166,7 @@ + def OnDoubleClick(self): + if not os.path.exists(self.file): + return +- edit = PyShell.flist.open(self.file) ++ edit = self.flist.open(self.file) + if hasattr(self.cl, 'lineno'): + lineno = self.cl.lineno + edit.gotoline(lineno) +@@ -199,10 +202,11 @@ + def OnDoubleClick(self): + if not os.path.exists(self.file): + return +- edit = PyShell.flist.open(self.file) ++ edit = self.flist.open(self.file) + edit.gotoline(self.cl.methods[self.name]) + + def main(): ++ import PyShell + try: + file = __file__ + except NameError: +Index: FileList.py +=================================================================== +--- FileList.py (revision 63995) ++++ FileList.py (revision 65541) +@@ -1,7 +1,15 @@ + import os +-from Tkinter import * + import tkMessageBox + ++def _canonize(filename): ++ if not os.path.isabs(filename): ++ try: ++ pwd = os.getcwd() ++ except os.error: ++ pass ++ else: ++ filename = os.path.join(pwd, filename) ++ return os.path.normpath(filename) + + class FileList: + +@@ -16,7 +24,7 @@ + + def open(self, filename, action=None): + assert filename +- filename = self.canonize(filename) ++ filename = _canonize(filename) + if os.path.isdir(filename): + # This can happen when bad filename is passed on command line: + tkMessageBox.showerror( +@@ -29,9 +37,10 @@ + edit = self.dict[key] + edit.top.wakeup() + return edit ++ + if action: + # Don't create window, perform 'action', e.g. open in same window +- return action(filename) ++ return action(filename=filename) + else: + return self.EditorWindow(self, filename, key) + +@@ -62,20 +71,20 @@ + if not self.inversedict: + self.root.quit() + +- def filename_changed_edit(self, edit): +- edit.saved_change_hook() ++ def filename_changed_edit(self, page, editwin): ++ page.saved_change_hook(page.tab_initialized) + try: +- key = self.inversedict[edit] ++ key = self.inversedict[editwin] + except KeyError: + print "Don't know this EditorWindow object. (rename)" + return +- filename = edit.io.filename ++ filename = page.io.filename + if not filename: + if key: + del self.dict[key] +- self.inversedict[edit] = None ++ self.inversedict[editwin] = None + return +- filename = self.canonize(filename) ++ filename = _canonize(filename) + newkey = os.path.normcase(filename) + if newkey == key: + return +@@ -86,28 +95,19 @@ + "Name Conflict", + "You now have multiple edit windows open for %r" % (filename,), + master=self.root) +- self.dict[newkey] = edit +- self.inversedict[edit] = newkey ++ self.dict[newkey] = editwin ++ self.inversedict[editwin] = newkey + if key: + try: + del self.dict[key] + except KeyError: + pass + +- def canonize(self, filename): +- if not os.path.isabs(filename): +- try: +- pwd = os.getcwd() +- except os.error: +- pass +- else: +- filename = os.path.join(pwd, filename) +- return os.path.normpath(filename) + +- + def _test(): ++ import sys ++ from Tkinter import Tk + from EditorWindow import fixwordbreaks +- import sys + root = Tk() + fixwordbreaks(root) + root.withdraw() +Index: CallTips.py +=================================================================== +--- CallTips.py (revision 63995) ++++ CallTips.py (revision 65541) +@@ -22,12 +22,12 @@ + ]) + ] + +- def __init__(self, editwin=None): +- if editwin is None: # subprocess and test +- self.editwin = None ++ def __init__(self, editpage=None): ++ if editpage is None: # subprocess and test ++ self.editpage = None + return +- self.editwin = editwin +- self.text = editwin.text ++ self.editpage = editpage ++ self.text = editpage.text + self.calltip = None + self._make_calltip_window = self._make_tk_calltip_window + +@@ -66,7 +66,7 @@ + def open_calltip(self, evalfuncs): + self._remove_calltip_window() + +- hp = HyperParser(self.editwin, "insert") ++ hp = HyperParser(self.editpage, "insert") + sur_paren = hp.get_surrounding_brackets('(') + if not sur_paren: + return +@@ -95,7 +95,7 @@ + + """ + try: +- rpcclt = self.editwin.flist.pyshell.interp.rpcclt ++ rpcclt = self.editpage.editwin.flist.pyshell.interp.rpcclt + except: + rpcclt = None + if rpcclt: +Index: CodeContext.py +=================================================================== +--- CodeContext.py (revision 63995) ++++ CodeContext.py (revision 65541) +@@ -23,7 +23,7 @@ + getspacesfirstword =\ + lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups() + +-class CodeContext: ++class CodeContext(object): + menudefs = [('options', [('!Code Conte_xt', '<>')])] + context_depth = idleConf.GetOption("extensions", "CodeContext", + "numlines", type="int", default=3) +@@ -31,10 +31,10 @@ + "bgcolor", type="str", default="LightGray") + fgcolor = idleConf.GetOption("extensions", "CodeContext", + "fgcolor", type="str", default="Black") +- def __init__(self, editwin): +- self.editwin = editwin +- self.text = editwin.text +- self.textfont = self.text["font"] ++ ++ def __init__(self, editpage): ++ self.editpage = editpage ++ self.editwin = editpage.editwin + self.label = None + # self.info is a list of (line number, indent level, line text, block + # keyword) tuples providing the block structure associated with +@@ -43,14 +43,11 @@ + # starts the toplevel 'block' of the module. + self.info = [(0, -1, "", False)] + self.topvisible = 1 +- visible = idleConf.GetOption("extensions", "CodeContext", +- "visible", type="bool", default=False) ++ visible = idleConf.GetOption("extensions", "CodeContext", "visible", ++ type="bool", default=False) + if visible: + self.toggle_code_context_event() + self.editwin.setvar('<>', True) +- # Start two update cycles, one for context lines, one for font changes. +- self.text.after(UPDATEINTERVAL, self.timer_event) +- self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) + + def toggle_code_context_event(self, event=None): + if not self.label: +@@ -59,33 +56,27 @@ + # + # All values are passed through int(str()), since some + # values may be pixel objects, which can't simply be added to ints. +- widgets = self.editwin.text, self.editwin.text_frame +- # Calculate the required vertical padding +- padx = 0 +- for widget in widgets: +- padx += int(str( widget.pack_info()['padx'] )) +- padx += int(str( widget.cget('padx') )) +- # Calculate the required border width +- border = 0 +- for widget in widgets: +- border += int(str( widget.cget('border') )) ++ ++ # Calculate the required horizontal padding ++ padx = int(str(self.editwin.text_notebook.pack_info()['padx'])) ++ + self.label = Tkinter.Label(self.editwin.top, + text="\n" * (self.context_depth - 1), + anchor=W, justify=LEFT, + font=self.textfont, + bg=self.bgcolor, fg=self.fgcolor, + width=1, #don't request more than we get +- padx=padx, border=border, ++ padx=padx, + relief=SUNKEN) +- # Pack the label widget before and above the text_frame widget, +- # thus ensuring that it will appear directly above text_frame ++ # Pack the label widget before and above the text_notebook widget, ++ # thus ensuring that it will appear directly above text_notebook + self.label.pack(side=TOP, fill=X, expand=False, +- before=self.editwin.text_frame) ++ before=self.editwin.text_notebook) + else: + self.label.destroy() + self.label = None + idleConf.SetOption("extensions", "CodeContext", "visible", +- str(self.label is not None)) ++ str(self.label is not None)) + idleConf.SaveUserCfgFiles() + + def get_line_info(self, linenum): +@@ -132,15 +123,12 @@ + return lines, lastindent + + def update_code_context(self): +- """Update context information and lines visible in the context pane. +- +- """ ++ """Update context information and lines visible in the context pane.""" + new_topvisible = int(self.text.index("@0,0").split('.')[0]) + if self.topvisible == new_topvisible: # haven't scrolled + return + if self.topvisible < new_topvisible: # scroll down +- lines, lastindent = self.get_context(new_topvisible, +- self.topvisible) ++ lines, lastindent = self.get_context(new_topvisible, self.topvisible) + # retain only context info applicable to the region + # between topvisible and new_topvisible: + while self.info[-1][1] >= lastindent: +@@ -153,8 +141,7 @@ + stopindent = self.info[-1][1] + del self.info[-1] + lines, lastindent = self.get_context(new_topvisible, +- self.info[-1][0]+1, +- stopindent) ++ self.info[-1][0] + 1, stopindent) + self.info.extend(lines) + self.topvisible = new_topvisible + # empty lines in context pane: +@@ -174,3 +161,18 @@ + self.textfont = newtextfont + self.label["font"] = self.textfont + self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) ++ ++ # Private methods ++ ++ def _get_editpage(self): ++ return self._editpage ++ ++ def _set_editpage(self, page): ++ self._editpage = page ++ self.text = page.text ++ self.textfont = self.text["font"] ++ # Start two update cycles, one for context lines, one for font changes. ++ self.text.after(UPDATEINTERVAL, self.timer_event) ++ self.text.after(FONTUPDATEINTERVAL, self.font_timer_event) ++ ++ editpage = property(_get_editpage, _set_editpage) +Index: textView.py +=================================================================== +--- textView.py (revision 63995) ++++ textView.py (revision 65541) +@@ -1,10 +1,16 @@ +-"""Simple text browser for IDLE ++"""Simple text browser for IDLE""" + +-""" +- +-from Tkinter import * + import tkMessageBox ++from Tkinter import Toplevel, Frame, Button, Scrollbar, Text ++from Tkconstants import DISABLED, SUNKEN, VERTICAL, WORD, RIGHT, Y, TOP, \ ++ LEFT, BOTH, BOTTOM + ++from configHandler import idleConf ++ ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Frame, Button, Scrollbar ++ + class TextViewer(Toplevel): + """A simple text viewer dialog for IDLE + +@@ -40,19 +46,22 @@ + frameText = Frame(self, relief=SUNKEN, height=700) + frameButtons = Frame(self) + self.buttonOk = Button(frameButtons, text='Close', +- command=self.Ok, takefocus=FALSE) ++ command=self.Ok, takefocus=False) + self.scrollbarView = Scrollbar(frameText, orient=VERTICAL, +- takefocus=FALSE, highlightthickness=0) +- self.textView = Text(frameText, wrap=WORD, highlightthickness=0, +- fg=self.fg, bg=self.bg) ++ takefocus=False) ++ self.textView = Text(frameText, wrap=WORD, fg=self.fg, bg=self.bg, ++ highlightthickness=0) + self.scrollbarView.config(command=self.textView.yview) + self.textView.config(yscrollcommand=self.scrollbarView.set) + self.buttonOk.pack() + self.scrollbarView.pack(side=RIGHT,fill=Y) +- self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH) +- frameButtons.pack(side=BOTTOM,fill=X) +- frameText.pack(side=TOP,expand=TRUE,fill=BOTH) ++ self.textView.pack(side=LEFT,expand=True,fill=BOTH) ++ frameButtons.pack(side=BOTTOM) ++ frameText.pack(side=TOP,expand=True,fill=BOTH) + ++ if TTK: ++ frameButtons['style'] = 'RootColor.TFrame' ++ + def Ok(self, event=None): + self.destroy() + +@@ -68,7 +77,6 @@ + else: + textFile = open(filename, 'r') + except IOError: +- import tkMessageBox + tkMessageBox.showerror(title='File Load Error', + message='Unable to load file %r .' % filename, + parent=parent) +@@ -77,6 +85,7 @@ + + + if __name__ == '__main__': ++ from Tkinter import Tk + #test the dialog + root=Tk() + root.title('textView test') +Index: SearchDialogBase.py +=================================================================== +--- SearchDialogBase.py (revision 63995) ++++ SearchDialogBase.py (revision 65541) +@@ -1,35 +1,42 @@ +-from Tkinter import * ++from Tkinter import Toplevel, Frame, Label, Entry, Button, Checkbutton, \ ++ Radiobutton + ++from configHandler import idleConf ++ ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Frame, Label, Entry, Button, Checkbutton, Radiobutton ++ + class SearchDialogBase: + + title = "Search Dialog" + icon = "Search" + needwrapbutton = 1 ++ bottom_btns = None + + def __init__(self, root, engine): + self.root = root + self.engine = engine +- self.top = None ++ self.ttop = None + + def open(self, text, searchphrase=None): + self.text = text +- if not self.top: ++ if not self.ttop: + self.create_widgets() + else: +- self.top.deiconify() +- self.top.tkraise() ++ self.ttop.deiconify() ++ self.ttop.tkraise() + if searchphrase: +- self.ent.delete(0,"end") +- self.ent.insert("end",searchphrase) ++ self.ent.delete(0, "end") ++ self.ent.insert("end", searchphrase) + self.ent.focus_set() + self.ent.selection_range(0, "end") + self.ent.icursor(0) +- self.top.grab_set() ++ self.ttop.grab_set() + + def close(self, event=None): +- if self.top: +- self.top.grab_release() +- self.top.withdraw() ++ if self.ttop: ++ self.ttop.grab_release() ++ self.ttop.withdraw() + + def create_widgets(self): + top = Toplevel(self.root) +@@ -38,103 +45,96 @@ + top.protocol("WM_DELETE_WINDOW", self.close) + top.wm_title(self.title) + top.wm_iconname(self.icon) +- self.top = top ++ top.resizable(height=False, width=False) ++ self.ttop = top ++ self.top = Frame(top) + + self.row = 0 +- self.top.grid_columnconfigure(0, pad=2, weight=0) +- self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100) ++ self.top.grid(sticky='news') + + self.create_entries() + self.create_option_buttons() + self.create_other_buttons() +- return self.create_command_buttons() ++ self.create_command_buttons() + ++ + def make_entry(self, label, var): + l = Label(self.top, text=label) +- l.grid(row=self.row, column=0, sticky="nw") ++ l.grid(row=self.row, column=0, sticky="ne", padx=6, pady=6) + e = Entry(self.top, textvariable=var, exportselection=0) +- e.grid(row=self.row, column=1, sticky="nwe") ++ e.grid(row=self.row, column=1, sticky="nwe", padx=6, pady=6) + self.row = self.row + 1 + return e + + def make_frame(self,labeltext=None): + if labeltext: + l = Label(self.top, text=labeltext) +- l.grid(row=self.row, column=0, sticky="nw") ++ l.grid(row=self.row, column=0, sticky="ne", padx=6, pady=6) + f = Frame(self.top) +- f.grid(row=self.row, column=1, columnspan=1, sticky="nwe") ++ f.grid(row=self.row, column=1, columnspan=1, sticky="nwe", ++ padx=6, pady=6 if labeltext else 0) + self.row = self.row + 1 + return f + +- def make_button(self, label, command, isdef=0): +- b = Button(self.buttonframe, +- text=label, command=command, +- default=isdef and "active" or "normal") +- cols,rows=self.buttonframe.grid_size() +- b.grid(pady=1,row=rows,column=0,sticky="ew") +- self.buttonframe.grid(rowspan=rows+1) +- return b +- + def create_entries(self): +- self.ent = self.make_entry("Find:", self.engine.patvar) ++ self.ent = self.make_entry("Find", self.engine.patvar) + + def create_option_buttons(self): + f = self.make_frame("Options") + +- btn = Checkbutton(f, anchor="w", +- variable=self.engine.revar, +- text="Regular expression") ++ btn = Checkbutton(f, variable=self.engine.revar, ++ text="Regular expression") + btn.pack(side="left", fill="both") + if self.engine.isre(): +- btn.select() ++ btn.invoke() + +- btn = Checkbutton(f, anchor="w", +- variable=self.engine.casevar, +- text="Match case") ++ btn = Checkbutton(f, variable=self.engine.casevar, text="Match case") + btn.pack(side="left", fill="both") + if self.engine.iscase(): +- btn.select() ++ btn.invoke() + +- btn = Checkbutton(f, anchor="w", +- variable=self.engine.wordvar, +- text="Whole word") ++ btn = Checkbutton(f, variable=self.engine.wordvar, text="Whole word") + btn.pack(side="left", fill="both") + if self.engine.isword(): +- btn.select() ++ btn.invoke() + + if self.needwrapbutton: +- btn = Checkbutton(f, anchor="w", +- variable=self.engine.wrapvar, +- text="Wrap around") ++ btn = Checkbutton(f, variable=self.engine.wrapvar, ++ text="Wrap around") + btn.pack(side="left", fill="both") + if self.engine.iswrap(): +- btn.select() ++ btn.invoke() + + def create_other_buttons(self): + f = self.make_frame("Direction") + +- #lbl = Label(f, text="Direction: ") +- #lbl.pack(side="left") +- +- btn = Radiobutton(f, anchor="w", +- variable=self.engine.backvar, value=1, +- text="Up") +- btn.pack(side="left", fill="both") ++ btn = Radiobutton(f, variable=self.engine.backvar, value=1, text="Up") ++ btn.pack(side="left") + if self.engine.isback(): +- btn.select() ++ btn.invoke() + +- btn = Radiobutton(f, anchor="w", +- variable=self.engine.backvar, value=0, +- text="Down") +- btn.pack(side="left", fill="both") ++ btn = Radiobutton(f, variable=self.engine.backvar, value=0, text="Down") ++ btn.pack(side="left") + if not self.engine.isback(): +- btn.select() ++ btn.invoke() + + def create_command_buttons(self): +- # +- # place button frame on the right +- f = self.buttonframe = Frame(self.top) +- f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2) ++ self.bottom_btns = self.bottom_btns or [] ++ f = Frame(self.top) ++ f.grid(row=self.row, column=0, columnspan=len(self.bottom_btns) + 1, ++ pady=6) + +- b = self.make_button("close", self.close) +- b.lower() ++ column = 0 ++ b = Button(f, text="Close", command=self.close) ++ b.grid(row=self.row, column=column, padx=6, pady=6) ++ column += 1 ++ ++ btns = {} ++ for tbtn in self.bottom_btns: ++ opts = {'text': tbtn[0], 'command': getattr(self, tbtn[1])} ++ if len(tbtn) == 3: ++ opts['default'] = tbtn[2] and 'active' or 'normal' ++ ++ btns[opts['text']] = Button(f, **opts).grid(row=self.row, padx=6, ++ pady=6, column=column) ++ column += 1 +Index: CallTipWindow.py +=================================================================== +--- CallTipWindow.py (revision 63995) ++++ CallTipWindow.py (revision 65541) +@@ -4,8 +4,14 @@ + Used by the CallTips IDLE extension. + + """ +-from Tkinter import * ++from Tkinter import Toplevel, Label, TclError ++from Tkconstants import SOLID, LEFT + ++from configHandler import idleConf ++ ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Label ++ + HIDE_VIRTUAL_EVENT_NAME = "<>" + HIDE_SEQUENCES = ("", "") + CHECKHIDE_VIRTUAL_EVENT_NAME = "<>" +@@ -142,6 +148,8 @@ + # + class container: # Conceptually an editor_window + def __init__(self): ++ from Tkinter import Tk, Text ++ from Tkconstants import BOTH + root = Tk() + text = self.text = Text(root) + text.pack(side=LEFT, fill=BOTH, expand=1) +@@ -163,6 +171,8 @@ + def calltip_hide(self, event): + self.calltip.hidetip() + ++# XXX Bugged test ++ + def main(): + # Test code + c=container() +Index: SearchDialog.py +=================================================================== +--- SearchDialog.py (revision 63995) ++++ SearchDialog.py (revision 65541) +@@ -1,8 +1,8 @@ +-from Tkinter import * ++from Tkinter import TclError ++ + import SearchEngine + from SearchDialogBase import SearchDialogBase + +- + def _setup(text): + root = text._root() + engine = SearchEngine.get(root) +@@ -12,7 +12,7 @@ + + def find(text): + pat = text.get("sel.first", "sel.last") +- return _setup(text).open(text,pat) ++ return _setup(text).open(text, pat) + + def find_again(text): + return _setup(text).find_again(text) +@@ -21,10 +21,10 @@ + return _setup(text).find_selection(text) + + class SearchDialog(SearchDialogBase): ++ bottom_btns = [("Find", 'default_command', 1)] + + def create_widgets(self): +- f = SearchDialogBase.create_widgets(self) +- self.make_button("Find", self.default_command, 1) ++ SearchDialogBase.create_widgets(self) + + def default_command(self, event=None): + if not self.engine.getprog(): +Index: idlever.py +=================================================================== +--- idlever.py (revision 63995) ++++ idlever.py (revision 65541) +@@ -1 +1 @@ +-IDLE_VERSION = "2.6a3" ++IDLE_VERSION = "2.6a3-gpolo" +Index: tabbedpages_old.py +=================================================================== +--- tabbedpages_old.py (revision 0) ++++ tabbedpages_old.py (revision 65541) +@@ -0,0 +1,546 @@ ++"""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. ++TabSet -- A widget containing tabs (buttons) in one or more rows. ++ ++""" ++from Tkinter import Frame, Radiobutton ++from Tkconstants import BOTH, TOP, X, RAISED, NSEW, FLAT, LEFT ++ ++from tabbedpages import InvalidNameError, AlreadyExistsError ++ ++ ++class TabSet(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 TabSet 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 TabSet. 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 named """ ++ if not tab_name in self._tab_names: ++ raise KeyError("No such Tab: '%s" % tab_name) ++ ++ self._tab_names.remove(tab_name) ++ self._arrange_tabs() ++ ++ def set_selected_tab(self, tab_name): ++ """Show the tab named as the selected one""" ++ 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" % tab_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) ++ self._tab_rows.append(tab_row) ++ ++ for tab_name in tab_names: ++ tab = TabSet.TabButton(tab_name, self.select_command, ++ tab_row, self) ++ 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 the last one created in the above loop ++ 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 ++ while self._tabs: ++ self._tabs.popitem()[1].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 ++ ++ # not expanding the tabs with more than one row is very ugly ++ expand_tabs = self.expand_tabs or n_rows > 1 ++ i = 0 # index in self._tab_names ++ for row_index in range(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.set_selected_tab(None) ++ if selected in self._tab_names: ++ self.set_selected_tab(selected) ++ ++ class TabButton(Frame): ++ """A simple tab-like widget.""" ++ ++ bw = 2 # borderwidth ++ ++ def __init__(self, name, select_command, tab_row, tab_set): ++ """Constructor arguments: ++ ++ name -- The tab's name, which will appear in its button. ++ ++ select_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, relief=RAISED) ++ ++ self.name = name ++ self.select_command = select_command ++ self.tab_set = tab_set ++ self.is_last_in_row = False ++ ++ self.button = Radiobutton( ++ self, text=name, command=self._select_event, ++ padx=5, pady=1, takefocus=False, indicatoron=False, ++ highlightthickness=0, selectcolor='', borderwidth=0) ++ self.button.pack(side=LEFT, fill=X, expand=True) ++ ++ self._init_masks() ++ self.set_normal() ++ ++ def _select_event(self, *args): ++ """Event handler for tab selection. ++ ++ With TabbedPageSet, this calls TabbedPageSet.change_page, so that ++ selecting a tab changes the page. ++ ++ Note that this does -not- call set_selected -- it will be called by ++ TabSet.set_selected_tab, which should be called when whatever the ++ tabs are related to changes. ++ ++ """ ++ self.select_command(self.name) ++ return ++ ++ def set_selected(self): ++ """Assume selected look""" ++ self._place_masks(selected=True) ++ ++ def set_normal(self): ++ """Assume normal look""" ++ 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 TabSet which will ++ manage the tabs. See TabSet'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 = TabSet(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 update_tabtitle(self, tab, newtitle): ++ """Update tab title to newtitle.""" ++ currpage = self.pages[tab.title] ++ old = tab.title ++ ++ # resolve title duplicate ++ if newtitle in self.pages: ++ count = 1 ++ temptitle = newtitle ++ while temptitle in self.pages: ++ temptitle = "%s #%d" % (newtitle, count) ++ count += 1 ++ newtitle = temptitle ++ ++ tab.title = newtitle ++ # now update 1 million places.. yeh.. ++ self.pages[newtitle] = self.pages.pop(old) ++ self._pages_order[self._pages_order.index(old)] = newtitle ++ self._tab_set._tab_names[self._tab_set._tab_names.index(old)] = newtitle ++ self._tab_set._tabs[newtitle] = self._tab_set._tabs.pop(old) ++ self._tab_set._tabs[newtitle].button['text'] = newtitle ++ if self._tab_set._selected_tab == old: ++ self._tab_set._selected_tab = newtitle ++ if self._current_page == old: ++ self._current_page = newtitle ++ if self._default_page == old: ++ self._default_page = newtitle ++ ++ 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) ++ ++ return self.pages[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.set_selected_tab(page_name) ++ self.event_generate('<>') # conform to ttk.Notebook ++ ++ def last_page(self): ++ return self.pages[self._pages_order[-1]] ++ ++ # Some methods to make this Notebook compatible with the ttk Notebook ++ ++ def select(self, page_id=None): ++ """Return the name of the currently selected page, otherwise ++ selects page_id. ++ ++ page_id may be an integer or a page name.""" ++ if page_id is None: ++ return self._current_page ++ elif isinstance(page_id, int): ++ self.change_page(self._pages_order[page_id]) ++ elif isinstance(page_id, str): ++ self.change_page(page_id) ++ ++ def index(self, page_name): ++ """Return the index of page_name.""" ++ return self._pages_order.index(page_name) ++ ++ def tabs(self): ++ """Return a list of page names.""" ++ return self._pages_order ++ ++if __name__ == '__main__': ++ from Tkinter import Tk, Label, Entry, Button ++ # 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() +Index: utils.py +=================================================================== +--- utils.py (revision 0) ++++ utils.py (revision 65541) +@@ -0,0 +1,4 @@ ++def callback(func, *myargs): ++ def w(*args): ++ return func(*(args + myargs)) ++ return w +Index: TreeWidget.py +=================================================================== +--- TreeWidget.py (revision 63995) ++++ TreeWidget.py (revision 65541) +@@ -15,12 +15,16 @@ + # - optimize tree redraw after expand of subnode + + import os +-from Tkinter import * +-import imp ++from Tkinter import Tk, Label, Entry, Frame, Canvas, Scrollbar, PhotoImage ++from Tkconstants import ALL, END + + import ZoomHeight + from configHandler import idleConf + ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import Label, Entry, Frame, Scrollbar ++ + ICONDIR = "Icons" + + # Look for Icons subdirectory in the same directory as this module +@@ -248,7 +252,9 @@ + label = self.label + except AttributeError: + # padding carefully selected (on Windows) to match Entry widget: +- self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2) ++ self.label = Label(self.canvas, text=text) ++ if not TTK: ++ self.label.configure(bd=0, padx=2, pady=2) + theme = idleConf.GetOption('main','Theme','name') + if self.selected: + self.label.configure(idleConf.GetHighlight(theme, 'hilite')) +@@ -451,7 +457,10 @@ + + # Testing functions + ++# XXX Can't run these tests ++ + def test(): ++ from Tkinter import Toplevel + import PyShell + root = Toplevel(PyShell.root) + root.configure(bd=0, bg="yellow") +Index: run.py +=================================================================== +--- run.py (revision 63995) ++++ run.py (revision 65541) +@@ -24,11 +24,13 @@ + except ImportError: + pass + else: +- def idle_formatwarning_subproc(message, category, filename, lineno): ++ def idle_formatwarning_subproc(message, category, filename, lineno, ++ line=None): + """Format warnings the IDLE way""" + s = "\nWarning (from warnings module):\n" + s += ' File \"%s\", line %s\n' % (filename, lineno) +- line = linecache.getline(filename, lineno).strip() ++ if line is None: ++ line = linecache.getline(filename, lineno).strip() + if line: + s += " %s\n" % line + s += "%s: %s\n" % (category.__name__, message) +Index: AutoExpand.py +=================================================================== +--- AutoExpand.py (revision 63995) ++++ AutoExpand.py (revision 65541) +@@ -15,8 +15,8 @@ + + wordchars = string.ascii_letters + string.digits + "_" + +- def __init__(self, editwin): +- self.text = editwin.text ++ def __init__(self, editpage): ++ self.text = editpage.text + self.state = None + + def expand_word_event(self, event): +Index: Percolator.py +=================================================================== +--- Percolator.py (revision 63995) ++++ Percolator.py (revision 65541) +@@ -1,5 +1,5 @@ ++from Delegator import Delegator + from WidgetRedirector import WidgetRedirector +-from Delegator import Delegator + + class Percolator: + +@@ -81,5 +81,5 @@ + root.mainloop() + + if __name__ == "__main__": +- from Tkinter import * ++ from Tkinter import Tk, Text + main() +Index: tabbedpages_new.py +=================================================================== +--- tabbedpages_new.py (revision 0) ++++ tabbedpages_new.py (revision 65541) +@@ -0,0 +1,116 @@ ++"""Classes exported: ++ ++TabbedPageSet -- A custom ttk.Notebook used by IDLE. ++""" ++from ttk import Frame, Notebook ++ ++from tabbedpages import InvalidNameError, AlreadyExistsError ++ ++class FramePage(object): ++ def __init__(self, notebook): ++ self.frame = Frame(notebook) ++ ++class TabbedPageSet(Notebook): ++ """ ++ 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 ttk's Frame widget. ++ ++ Pages may be added or removed at any time using the add_page() and ++ remove_page() methods. ++ """ ++ ++ def __init__(self, master, page_names=None, **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. ++ """ ++ Notebook.__init__(self, master, **kw) ++ ++ self.pages = {} ++ page_names = page_names or () ++ for name in page_names: ++ self.add_page(name) ++ ++ def update_tabtitle(self, tab, newtitle): ++ """Update tab title to newtitle.""" ++ currpage = self.pages[tab.title].frame ++ old = tab.title ++ ++ # resolve title duplicate ++ if newtitle in self.pages and currpage != self.pages[newtitle].frame: ++ # newtitle is already present, and the current tab is not the ++ # one who owns it ++ count = 1 ++ temptitle = newtitle ++ while temptitle in self.pages: ++ if currpage == self.pages[temptitle].frame: ++ break ++ temptitle = "%s #%d" % (newtitle, count) ++ count += 1 ++ newtitle = temptitle ++ ++ tab.title = newtitle ++ self.pages[newtitle] = self.pages.pop(old) ++ self.tab(currpage, text=newtitle) ++ ++ 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) ++ ++ fpage = FramePage(self) ++ self.pages[page_name] = fpage ++ self.add(fpage.frame, text=page_name, padding=6) ++ ++ # workaround for bug #1878298 at tktoolkit sf bug tracker ++ self.event_generate('') ++ ++ return fpage ++ ++ def remove_page(self, page_name): ++ """Remove page_name from the notebook.""" ++ if not page_name in self.pages: ++ raise KeyError("No such TabPage: '%s" % page_name) ++ ++ self.forget(self.index(self.pages[page_name].frame)) ++ del self.pages[page_name] ++ ++ # workaround for bug #1878298 at tktoolkit sf bug tracker ++ self.event_generate('') ++ ++ def last_page(self): ++ """Return the last page in the notebook.""" ++ return self.pages[self.tab(self.index('end') - 1)['text']] ++ ++if __name__ == '__main__': ++ from Tkinter import Tk ++ from Tkconstants import TOP, BOTH ++ from ttk import Label, Entry, Button, Style ++ # test dialog ++ root=Tk() ++ style = Style() ++ style.configure('C.TLabel', padding=20) ++ tabPage=TabbedPageSet(root, page_names=['Foobar','Baz']) ++ tabPage.pack(side=TOP, expand=True, fill=BOTH) ++ Label(tabPage.pages['Foobar'].frame, text='Foo', style='C.TLabel').pack() ++ Label(tabPage.pages['Foobar'].frame, text='Bar', style='C.TLabel').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() +Index: dynOptionMenuWidget.py +=================================================================== +--- dynOptionMenuWidget.py (revision 63995) ++++ dynOptionMenuWidget.py (revision 65541) +@@ -2,34 +2,41 @@ + OptionMenu widget modified to allow dynamic menu reconfiguration + and setting of highlightthickness + """ +-from Tkinter import OptionMenu ++from Tkinter import OptionMenu, Menu + from Tkinter import _setit + import copy + ++from configHandler import idleConf ++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int') ++if TTK: ++ from ttk import * ++ + class DynOptionMenu(OptionMenu): +- """ +- unlike OptionMenu, our kwargs can include highlightthickness +- """ ++ """Unlike OptionMenu, our kwargs can include highlightthickness""" + def __init__(self, master, variable, value, *values, **kwargs): + #get a copy of kwargs before OptionMenu.__init__ munges them + kwargsCopy=copy.copy(kwargs) + if 'highlightthickness' in kwargs.keys(): + del(kwargs['highlightthickness']) ++ self.command=kwargs.get('command') ++ self.variable=variable ++ + OptionMenu.__init__(self, master, variable, value, *values, **kwargs) + self.config(highlightthickness=kwargsCopy.get('highlightthickness')) +- #self.menu=self['menu'] +- self.variable=variable +- self.command=kwargs.get('command') + +- def SetMenu(self,valueList,value=None): ++ def SetMenu(self, valueList, value=None): + """ + clear and reload the menu with a new set of options. + valueList - list of new options + value - initial value to set the optionmenu's menubutton to + """ +- self['menu'].delete(0,'end') +- for item in valueList: +- self['menu'].add_command(label=item, ++ if TTK: ++ self.set_menu(value, *valueList) ++ else: ++ menu = self['menu'] ++ menu.delete(0,'end') ++ for item in valueList: ++ menu.add_command(label=item, + command=_setit(self.variable,item,self.command)) +- if value: +- self.variable.set(value) ++ if value: ++ self.variable.set(value) +Index: extend.txt +=================================================================== +--- extend.txt (revision 63995) ++++ extend.txt (revision 65541) +@@ -81,3 +81,10 @@ + + For further information on binding refer to the Tkinter Resources web page at + python.org and to the Tk Command "bind" man page. ++ ++ ++Note ++---- ++ ++Given that this branch is using tabbed pages, don't expect the previous ++description to be correct related to the code present here. +Index: MultiStatusBar.py +=================================================================== +--- MultiStatusBar.py (revision 63995) ++++ MultiStatusBar.py (revision 65541) +@@ -1,5 +1,11 @@ +-from Tkinter import * ++from Tkinter import Tk, Frame, Label ++from Tkconstants import LEFT, SUNKEN, W + ++from configHandler import idleConf ++ ++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'): ++ from ttk import Frame, Label ++ + class MultiStatusBar(Frame): + + def __init__(self, master=None, **kw): +@@ -10,7 +16,7 @@ + + def set_label(self, name, text='', side=LEFT): + if not self.labels.has_key(name): +- label = Label(self, bd=1, relief=SUNKEN, anchor=W) ++ label = Label(self, relief=SUNKEN, anchor=W) + label.pack(side=side) + self.labels[name] = label + else: +@@ -18,6 +24,8 @@ + label.config(text=text) + + def _test(): ++ from Tkinter import Text ++ from Tkconstants import TOP, BOTTOM, X + b = Frame() + c = Text(b) + c.pack(side=TOP) + +Property changes on: . +___________________________________________________________________ +Name: svnmerge-integrated + + /python/trunk/Lib/idlelib:1-63994 + From buildbot at python.org Tue Aug 5 04:03:41 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 02:03:41 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo trunk Message-ID: <20080805020341.304171E400A@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%20trunk/builds/4030 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-x86 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_ast test_parser ====================================================================== FAIL: test_snippets (test.test_ast.AST_Tests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_ast.py", line 146, in test_snippets self.assertEquals(to_tuple(ast_tree), o) AssertionError: ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 1127953869))], [])]) != ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 9))], [])]) ====================================================================== FAIL: test_copy_location (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_ast.py", line 215, in test_copy_location 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=1120660616), lineno=1, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, col_offset=0))' ====================================================================== FAIL: test_dump (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_ast.py", line 204, in test_dump "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=1137080649), Str(s='and cheese', lineno=1, col_offset=1137080655)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=5), Str(s='and cheese', lineno=1, col_offset=11)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_fix_missing_locations (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_ast.py", line 226, in test_fix_missing_locations "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=1080856394)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=6)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_increment_lineno (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_ast.py", line 240, in test_increment_lineno 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=1120660224), lineno=4, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, col_offset=0))' ====================================================================== FAIL: test_position (test.test_parser.RoundtripLegalSyntaxTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/buildslave/python-trunk/trunk.norwitz-x86/build/Lib/test/test_parser.py", line 222, in test_position terminals) AssertionError: [(1, 'def', 1, 0), (1, 'f', 1, 4), (7, '(', 1, 5), (1, 'x', 1, 6), (8, ')', 1, 7), (11, ':', 1, 8), (4, '', 1, 9), (5, '', 2, -1), (1, 'return', 2, 4), (1, 'x', 2, 11), (14, '+', 2, 13), (2, '1', 2, 15), (4, '', 2, 16), (6, '', 2, -1), (4, '', 2, -1), (0, '', 2, -1)] != [(1, 'def', 1, 0), (1, 'f', 1, 1137015448), (7, '(', 1, 1137015449), (1, 'x', 1, 1137015450), (8, ')', 1, 1137015451), (11, ':', 1, 1137015452), (4, '', 1, 1137015453), (5, '', 2, -1), (1, 'return', 2, 1137015458), (1, 'x', 2, 1137015465), (14, '+', 2, 1137015467), (2, '1', 2, 1137015469), (4, '', 2, 1137015470), (6, '', 2, 0), (4, '', 2, 0), (0, '', 2, 0)] make: *** [buildbottest] Error 1 sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 04:05:23 2008 From: python-checkins at python.org (andrew.kuchling) Date: Tue, 5 Aug 2008 04:05:23 +0200 (CEST) Subject: [Python-checkins] r65543 - python/trunk/Parser/tokenizer.c Message-ID: <20080805020523.881E61E4007@bag.python.org> Author: andrew.kuchling Date: Tue Aug 5 04:05:23 2008 New Revision: 65543 Log: #3367: revert rev. 65539: this change causes test_parser to fail Modified: python/trunk/Parser/tokenizer.c Modified: python/trunk/Parser/tokenizer.c ============================================================================== --- python/trunk/Parser/tokenizer.c (original) +++ python/trunk/Parser/tokenizer.c Tue Aug 5 04:05:23 2008 @@ -1117,7 +1117,7 @@ register int c; int blankline; - tok->line_start = *p_start = *p_end = NULL; + *p_start = *p_end = NULL; nextline: tok->start = NULL; blankline = 0; From buildbot at python.org Tue Aug 5 04:05:46 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 02:05:46 +0000 Subject: [Python-checkins] buildbot failure in OS X x86 trunk Message-ID: <20080805020547.250541E4007@bag.python.org> The Buildbot has detected a new failure of OS X x86 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/OS%20X%20x86%20trunk/builds/148 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: noller-osx86 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_ast test_parser ====================================================================== FAIL: test_snippets (test.test_ast.AST_Tests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/buildbot/buildarea/trunk.noller-osx86/build/Lib/test/test_ast.py", line 146, in test_snippets self.assertEquals(to_tuple(ast_tree), o) AssertionError: ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 62027149))], [])]) != ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 9))], [])]) ====================================================================== FAIL: test_copy_location (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/buildbot/buildarea/trunk.noller-osx86/build/Lib/test/test_ast.py", line 215, in test_copy_location 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=21102776), lineno=1, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, col_offset=0))' ====================================================================== FAIL: test_dump (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/buildbot/buildarea/trunk.noller-osx86/build/Lib/test/test_ast.py", line 204, in test_dump "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=7083641), Str(s='and cheese', lineno=1, col_offset=7083647)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=5), Str(s='and cheese', lineno=1, col_offset=11)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_fix_missing_locations (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/buildbot/buildarea/trunk.noller-osx86/build/Lib/test/test_ast.py", line 226, in test_fix_missing_locations "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=61909322)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=6)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_increment_lineno (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/buildbot/buildarea/trunk.noller-osx86/build/Lib/test/test_ast.py", line 240, in test_increment_lineno 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=21103168), lineno=4, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, col_offset=0))' ====================================================================== FAIL: test_position (test.test_parser.RoundtripLegalSyntaxTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/buildbot/buildarea/trunk.noller-osx86/build/Lib/test/test_parser.py", line 222, in test_position terminals) AssertionError: [(1, 'def', 1, 0), (1, 'f', 1, 4), (7, '(', 1, 5), (1, 'x', 1, 6), (8, ')', 1, 7), (11, ':', 1, 8), (4, '', 1, 9), (5, '', 2, -1), (1, 'return', 2, 4), (1, 'x', 2, 11), (14, '+', 2, 13), (2, '1', 2, 15), (4, '', 2, 16), (6, '', 2, -1), (4, '', 2, -1), (0, '', 2, -1)] != [(1, 'def', 1, 0), (1, 'f', 1, 7104216), (7, '(', 1, 7104217), (1, 'x', 1, 7104218), (8, ')', 1, 7104219), (11, ':', 1, 7104220), (4, '', 1, 7104221), (5, '', 2, -1), (1, 'return', 2, 7104226), (1, 'x', 2, 7104233), (14, '+', 2, 7104235), (2, '1', 2, 7104237), (4, '', 2, 7104238), (6, '', 2, 0), (4, '', 2, 0), (0, '', 2, 0)] make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 04:23:37 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 02:23:37 +0000 Subject: [Python-checkins] buildbot failure in ppc Debian unstable trunk Message-ID: <20080805022338.1602A1E4007@bag.python.org> The Buildbot has detected a new failure of ppc Debian unstable trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ppc%20Debian%20unstable%20trunk/builds/1823 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_ast test_parser ====================================================================== FAIL: test_snippets (test.test_ast.AST_Tests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/test/test_ast.py", line 146, in test_snippets self.assertEquals(to_tuple(ast_tree), o) AssertionError: ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 282097421))], [])]) != ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 9))], [])]) ====================================================================== FAIL: test_copy_location (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/test/test_ast.py", line 215, in test_copy_location 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=319572848), lineno=1, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, col_offset=0))' ====================================================================== FAIL: test_dump (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/test/test_ast.py", line 204, in test_dump "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=282037489), Str(s='and cheese', lineno=1, col_offset=282037495)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=5), Str(s='and cheese', lineno=1, col_offset=11)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_fix_missing_locations (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/test/test_ast.py", line 226, in test_fix_missing_locations "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=319485258)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=6)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_increment_lineno (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/test/test_ast.py", line 240, in test_increment_lineno 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=319573856), lineno=4, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, col_offset=0))' ====================================================================== FAIL: test_position (test.test_parser.RoundtripLegalSyntaxTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ppc/build/Lib/test/test_parser.py", line 222, in test_position terminals) AssertionError: [(1, 'def', 1, 0), (1, 'f', 1, 4), (7, '(', 1, 5), (1, 'x', 1, 6), (8, ')', 1, 7), (11, ':', 1, 8), (4, '', 1, 9), (5, '', 2, -1), (1, 'return', 2, 4), (1, 'x', 2, 11), (14, '+', 2, 13), (2, '1', 2, 15), (4, '', 2, 16), (6, '', 2, -1), (4, '', 2, -1), (0, '', 2, -1)] != [(1, 'def', 1, 0), (1, 'f', 1, 290871640), (7, '(', 1, 290871641), (1, 'x', 1, 290871642), (8, ')', 1, 290871643), (11, ':', 1, 290871644), (4, '', 1, 290871645), (5, '', 2, -1), (1, 'return', 2, 290871650), (1, 'x', 2, 290871657), (14, '+', 2, 290871659), (2, '1', 2, 290871661), (4, '', 2, 290871662), (6, '', 2, 0), (4, '', 2, 0), (0, '', 2, 0)] make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 04:30:25 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 02:30:25 +0000 Subject: [Python-checkins] buildbot failure in PPC64 Debian trunk Message-ID: <20080805023025.743C61E4007@bag.python.org> The Buildbot has detected a new failure of PPC64 Debian trunk. Full details are available at: http://www.python.org/dev/buildbot/all/PPC64%20Debian%20trunk/builds/1275 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ppc64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_ast test_parser ====================================================================== FAIL: test_snippets (test.test_ast.AST_Tests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea64/trunk.klose-debian-ppc64/build/Lib/test/test_ast.py", line 146, in test_snippets self.assertEquals(to_tuple(ast_tree), o) AssertionError: ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 346754741))], [])]) != ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 9))], [])]) ====================================================================== FAIL: test_copy_location (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea64/trunk.klose-debian-ppc64/build/Lib/test/test_ast.py", line 215, in test_copy_location 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=346697016), lineno=1, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, col_offset=0))' ====================================================================== FAIL: test_dump (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea64/trunk.klose-debian-ppc64/build/Lib/test/test_ast.py", line 204, in test_dump "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=317899657), Str(s='and cheese', lineno=1, col_offset=317899663)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=5), Str(s='and cheese', lineno=1, col_offset=11)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_fix_missing_locations (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea64/trunk.klose-debian-ppc64/build/Lib/test/test_ast.py", line 226, in test_fix_missing_locations "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=346753282)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=6)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_increment_lineno (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea64/trunk.klose-debian-ppc64/build/Lib/test/test_ast.py", line 240, in test_increment_lineno 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=346697208), lineno=4, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, col_offset=0))' ====================================================================== FAIL: test_position (test.test_parser.RoundtripLegalSyntaxTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea64/trunk.klose-debian-ppc64/build/Lib/test/test_parser.py", line 222, in test_position terminals) AssertionError: [(1, 'def', 1, 0), (1, 'f', 1, 4), (7, '(', 1, 5), (1, 'x', 1, 6), (8, ')', 1, 7), (11, ':', 1, 8), (4, '', 1, 9), (5, '', 2, -1), (1, 'return', 2, 4), (1, 'x', 2, 11), (14, '+', 2, 13), (2, '1', 2, 15), (4, '', 2, 16), (6, '', 2, -1), (4, '', 2, -1), (0, '', 2, -1)] != [(1, 'def', 1, 0), (1, 'f', 1, 378792408), (7, '(', 1, 378792409), (1, 'x', 1, 378792410), (8, ')', 1, 378792411), (11, ':', 1, 378792412), (4, '', 1, 378792413), (5, '', 2, -1), (1, 'return', 2, 378792418), (1, 'x', 2, 378792425), (14, '+', 2, 378792427), (2, '1', 2, 378792429), (4, '', 2, 378792430), (6, '', 2, 0), (4, '', 2, 0), (0, '', 2, 0)] make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 04:44:33 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 02:44:33 +0000 Subject: [Python-checkins] buildbot failure in ia64 Ubuntu trunk Message-ID: <20080805024433.4048F1E4007@bag.python.org> The Buildbot has detected a new failure of ia64 Ubuntu trunk. Full details are available at: http://www.python.org/dev/buildbot/all/ia64%20Ubuntu%20trunk/builds/427 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: klose-debian-ia64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling BUILD FAILED: failed test Excerpt from the test logfile: 2 tests failed: test_ast test_parser ====================================================================== FAIL: test_snippets (test.test_ast.AST_Tests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_ast.py", line 146, in test_snippets self.assertEquals(to_tuple(ast_tree), o) AssertionError: ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 129396405))], [])]) != ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, []), [('Pass', (1, 9))], [])]) ====================================================================== FAIL: test_copy_location (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_ast.py", line 215, in test_copy_location 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=110398872), lineno=1, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, col_offset=0))' ====================================================================== FAIL: test_dump (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_ast.py", line 204, in test_dump "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=61857177), Str(s='and cheese', lineno=1, col_offset=61857183)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), lineno=1, col_offset=5), Str(s='and cheese', lineno=1, col_offset=11)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_fix_missing_locations (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_ast.py", line 226, in test_fix_missing_locations "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), " AssertionError: "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=108212762)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" != "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, col_offset=6)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0), Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0), lineno=1, col_offset=0)])" ====================================================================== FAIL: test_increment_lineno (test.test_ast.ASTHelpers_Test) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_ast.py", line 240, in test_increment_lineno 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), ' AssertionError: 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=110398008), lineno=4, col_offset=0))' != 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, col_offset=0))' ====================================================================== FAIL: test_position (test.test_parser.RoundtripLegalSyntaxTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/pybot/buildarea/trunk.klose-debian-ia64/build/Lib/test/test_parser.py", line 222, in test_position terminals) AssertionError: [(1, 'def', 1, 0), (1, 'f', 1, 4), (7, '(', 1, 5), (1, 'x', 1, 6), (8, ')', 1, 7), (11, ':', 1, 8), (4, '', 1, 9), (5, '', 2, -1), (1, 'return', 2, 4), (1, 'x', 2, 11), (14, '+', 2, 13), (2, '1', 2, 15), (4, '', 2, 16), (6, '', 2, -1), (4, '', 2, -1), (0, '', 2, -1)] != [(1, 'def', 1, 0), (1, 'f', 1, 129409264), (7, '(', 1, 129409265), (1, 'x', 1, 129409266), (8, ')', 1, 129409267), (11, ':', 1, 129409268), (4, '', 1, 129409269), (5, '', 2, -1), (1, 'return', 2, 129409274), (1, 'x', 2, 129409281), (14, '+', 2, 129409283), (2, '1', 2, 129409285), (4, '', 2, 129409286), (6, '', 2, 0), (4, '', 2, 0), (0, '', 2, 0)] make: *** [buildbottest] Error 1 sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 05:19:55 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 03:19:55 +0000 Subject: [Python-checkins] buildbot failure in g4 osx.4 trunk Message-ID: <20080805031955.E0FEE1E4007@bag.python.org> The Buildbot has detected a new failure of g4 osx.4 trunk. Full details are available at: http://www.python.org/dev/buildbot/all/g4%20osx.4%20trunk/builds/3779 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: psf-g4 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: andrew.kuchling,brett.cannon BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From python-checkins at python.org Tue Aug 5 05:39:22 2008 From: python-checkins at python.org (guido.van.rossum) Date: Tue, 5 Aug 2008 05:39:22 +0200 (CEST) Subject: [Python-checkins] r65544 - python/trunk/Modules/_sre.c Message-ID: <20080805033922.3533A1E4007@bag.python.org> Author: guido.van.rossum Date: Tue Aug 5 05:39:21 2008 New Revision: 65544 Log: Tracker issue 3487: sre "bytecode" verifier. This is a verifier for the binary code used by the _sre module (this is often called bytecode, though to distinguish it from Python bytecode I put it in quotes). I wrote this for Google App Engine, and am making the patch available as open source under the Apache 2 license. Below are the copyright statement and license, for completeness. # Copyright 2008 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. It's not necessary to include these copyrights and bytecode in the source file. Google has signed a contributor's agreement with the PSF already. Modified: python/trunk/Modules/_sre.c Modified: python/trunk/Modules/_sre.c ============================================================================== --- python/trunk/Modules/_sre.c (original) +++ python/trunk/Modules/_sre.c Tue Aug 5 05:39:21 2008 @@ -2658,6 +2658,8 @@ offsetof(PatternObject, weakreflist), /* tp_weaklistoffset */ }; +static int _validate(PatternObject *self); /* Forward */ + static PyObject * _compile(PyObject* self_, PyObject* args) { @@ -2717,10 +2719,482 @@ self->weakreflist = NULL; + if (!_validate(self)) { + Py_DECREF(self); + return NULL; + } + return (PyObject*) self; } /* -------------------------------------------------------------------- */ +/* Code validation */ + +/* To learn more about this code, have a look at the _compile() function in + Lib/sre_compile.py. The validation functions below checks the code array + for conformance with the code patterns generated there. + + The nice thing about the generated code is that it is position-independent: + all jumps are relative jumps forward. Also, jumps don't cross each other: + the target of a later jump is always earlier than the target of an earlier + jump. IOW, this is okay: + + J---------J-------T--------T + \ \_____/ / + \______________________/ + + but this is not: + + J---------J-------T--------T + \_________\_____/ / + \____________/ + + It also helps that SRE_CODE is always an unsigned type, either 2 bytes or 4 + bytes wide (the latter if Python is compiled for "wide" unicode support). +*/ + +/* Defining this one enables tracing of the validator */ +#undef VVERBOSE + +/* Trace macro for the validator */ +#if defined(VVERBOSE) +#define VTRACE(v) printf v +#else +#define VTRACE(v) +#endif + +/* Report failure */ +#define FAIL do { VTRACE(("FAIL: %d\n", __LINE__)); return 0; } while (0) + +/* Extract opcode, argument, or skip count from code array */ +#define GET_OP \ + do { \ + VTRACE(("%p: ", code)); \ + if (code >= end) FAIL; \ + op = *code++; \ + VTRACE(("%lu (op)\n", (unsigned long)op)); \ + } while (0) +#define GET_ARG \ + do { \ + VTRACE(("%p= ", code)); \ + if (code >= end) FAIL; \ + arg = *code++; \ + VTRACE(("%lu (arg)\n", (unsigned long)arg)); \ + } while (0) +#define GET_SKIP \ + do { \ + VTRACE(("%p= ", code)); \ + if (code >= end) FAIL; \ + skip = *code; \ + VTRACE(("%lu (skip to %p)\n", \ + (unsigned long)skip, code+skip)); \ + if (code+skip < code || code+skip > end) \ + FAIL; \ + code++; \ + } while (0) + +static int +_validate_charset(SRE_CODE *code, SRE_CODE *end) +{ + /* Some variables are manipulated by the macros above */ + SRE_CODE op; + SRE_CODE arg; + SRE_CODE offset; + int i; + + while (code < end) { + GET_OP; + switch (op) { + + case SRE_OP_NEGATE: + break; + + case SRE_OP_LITERAL: + GET_ARG; + break; + + case SRE_OP_RANGE: + GET_ARG; + GET_ARG; + break; + + case SRE_OP_CHARSET: + offset = 32/sizeof(SRE_CODE); /* 32-byte bitmap */ + if (code+offset < code || code+offset > end) + FAIL; + code += offset; + break; + + case SRE_OP_BIGCHARSET: + GET_ARG; /* Number of blocks */ + offset = 256/sizeof(SRE_CODE); /* 256-byte table */ + if (code+offset < code || code+offset > end) + FAIL; + /* Make sure that each byte points to a valid block */ + for (i = 0; i < 256; i++) { + if (((unsigned char *)code)[i] >= arg) + FAIL; + } + code += offset; + offset = arg * 32/sizeof(SRE_CODE); /* 32-byte bitmap times arg */ + if (code+offset < code || code+offset > end) + FAIL; + code += offset; + break; + + case SRE_OP_CATEGORY: + GET_ARG; + switch (arg) { + case SRE_CATEGORY_DIGIT: + case SRE_CATEGORY_NOT_DIGIT: + case SRE_CATEGORY_SPACE: + case SRE_CATEGORY_NOT_SPACE: + case SRE_CATEGORY_WORD: + case SRE_CATEGORY_NOT_WORD: + case SRE_CATEGORY_LINEBREAK: + case SRE_CATEGORY_NOT_LINEBREAK: + case SRE_CATEGORY_LOC_WORD: + case SRE_CATEGORY_LOC_NOT_WORD: + case SRE_CATEGORY_UNI_DIGIT: + case SRE_CATEGORY_UNI_NOT_DIGIT: + case SRE_CATEGORY_UNI_SPACE: + case SRE_CATEGORY_UNI_NOT_SPACE: + case SRE_CATEGORY_UNI_WORD: + case SRE_CATEGORY_UNI_NOT_WORD: + case SRE_CATEGORY_UNI_LINEBREAK: + case SRE_CATEGORY_UNI_NOT_LINEBREAK: + break; + default: + FAIL; + } + break; + + default: + FAIL; + + } + } + + return 1; +} + +static int +_validate_inner(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups) +{ + /* Some variables are manipulated by the macros above */ + SRE_CODE op; + SRE_CODE arg; + SRE_CODE skip; + + VTRACE(("code=%p, end=%p\n", code, end)); + + if (code > end) + FAIL; + + while (code < end) { + GET_OP; + switch (op) { + + case SRE_OP_MARK: + /* We don't check whether marks are properly nested; the + sre_match() code is robust even if they don't, and the worst + you can get is nonsensical match results. */ + GET_ARG; + if (arg > 2*groups+1) { + VTRACE(("arg=%d, groups=%d\n", (int)arg, (int)groups)); + FAIL; + } + break; + + case SRE_OP_LITERAL: + case SRE_OP_NOT_LITERAL: + case SRE_OP_LITERAL_IGNORE: + case SRE_OP_NOT_LITERAL_IGNORE: + GET_ARG; + /* The arg is just a character, nothing to check */ + break; + + case SRE_OP_SUCCESS: + case SRE_OP_FAILURE: + /* Nothing to check; these normally end the matching process */ + break; + + case SRE_OP_AT: + GET_ARG; + switch (arg) { + case SRE_AT_BEGINNING: + case SRE_AT_BEGINNING_STRING: + case SRE_AT_BEGINNING_LINE: + case SRE_AT_END: + case SRE_AT_END_LINE: + case SRE_AT_END_STRING: + case SRE_AT_BOUNDARY: + case SRE_AT_NON_BOUNDARY: + case SRE_AT_LOC_BOUNDARY: + case SRE_AT_LOC_NON_BOUNDARY: + case SRE_AT_UNI_BOUNDARY: + case SRE_AT_UNI_NON_BOUNDARY: + break; + default: + FAIL; + } + break; + + case SRE_OP_ANY: + case SRE_OP_ANY_ALL: + /* These have no operands */ + break; + + case SRE_OP_IN: + case SRE_OP_IN_IGNORE: + GET_SKIP; + /* Stop 1 before the end; we check the FAILURE below */ + if (!_validate_charset(code, code+skip-2)) + FAIL; + if (code[skip-2] != SRE_OP_FAILURE) + FAIL; + code += skip-1; + break; + + case SRE_OP_INFO: + { + /* A minimal info field is + <1=skip> <2=flags> <3=min> <4=max>; + If SRE_INFO_PREFIX or SRE_INFO_CHARSET is in the flags, + more follows. */ + SRE_CODE flags, min, max, i; + SRE_CODE *newcode; + GET_SKIP; + newcode = code+skip-1; + GET_ARG; flags = arg; + GET_ARG; min = arg; + GET_ARG; max = arg; + /* Check that only valid flags are present */ + if ((flags & ~(SRE_INFO_PREFIX | + SRE_INFO_LITERAL | + SRE_INFO_CHARSET)) != 0) + FAIL; + /* PREFIX and CHARSET are mutually exclusive */ + if ((flags & SRE_INFO_PREFIX) && + (flags & SRE_INFO_CHARSET)) + FAIL; + /* LITERAL implies PREFIX */ + if ((flags & SRE_INFO_LITERAL) && + !(flags & SRE_INFO_PREFIX)) + FAIL; + /* Validate the prefix */ + if (flags & SRE_INFO_PREFIX) { + SRE_CODE prefix_len, prefix_skip; + GET_ARG; prefix_len = arg; + GET_ARG; prefix_skip = arg; + /* Here comes the prefix string */ + if (code+prefix_len < code || code+prefix_len > newcode) + FAIL; + code += prefix_len; + /* And here comes the overlap table */ + if (code+prefix_len < code || code+prefix_len > newcode) + FAIL; + /* Each overlap value should be < prefix_len */ + for (i = 0; i < prefix_len; i++) { + if (code[i] >= prefix_len) + FAIL; + } + code += prefix_len; + } + /* Validate the charset */ + if (flags & SRE_INFO_CHARSET) { + if (!_validate_charset(code, newcode-1)) + FAIL; + if (newcode[-1] != SRE_OP_FAILURE) + FAIL; + code = newcode; + } + else if (code != newcode) { + VTRACE(("code=%p, newcode=%p\n", code, newcode)); + FAIL; + } + } + break; + + case SRE_OP_BRANCH: + { + SRE_CODE *target = NULL; + for (;;) { + GET_SKIP; + if (skip == 0) + break; + /* Stop 2 before the end; we check the JUMP below */ + if (!_validate_inner(code, code+skip-3, groups)) + FAIL; + code += skip-3; + /* Check that it ends with a JUMP, and that each JUMP + has the same target */ + GET_OP; + if (op != SRE_OP_JUMP) + FAIL; + GET_SKIP; + if (target == NULL) + target = code+skip-1; + else if (code+skip-1 != target) + FAIL; + } + } + break; + + case SRE_OP_REPEAT_ONE: + case SRE_OP_MIN_REPEAT_ONE: + { + SRE_CODE min, max; + GET_SKIP; + GET_ARG; min = arg; + GET_ARG; max = arg; + if (min > max) + FAIL; +#ifdef Py_UNICODE_WIDE + if (max > 65535) + FAIL; +#endif + if (!_validate_inner(code, code+skip-4, groups)) + FAIL; + code += skip-4; + GET_OP; + if (op != SRE_OP_SUCCESS) + FAIL; + } + break; + + case SRE_OP_REPEAT: + { + SRE_CODE min, max; + GET_SKIP; + GET_ARG; min = arg; + GET_ARG; max = arg; + if (min > max) + FAIL; +#ifdef Py_UNICODE_WIDE + if (max > 65535) + FAIL; +#endif + if (!_validate_inner(code, code+skip-3, groups)) + FAIL; + code += skip-3; + GET_OP; + if (op != SRE_OP_MAX_UNTIL && op != SRE_OP_MIN_UNTIL) + FAIL; + } + break; + + case SRE_OP_GROUPREF: + case SRE_OP_GROUPREF_IGNORE: + GET_ARG; + if (arg >= groups) + FAIL; + break; + + case SRE_OP_GROUPREF_EXISTS: + /* The regex syntax for this is: '(?(group)then|else)', where + 'group' is either an integer group number or a group name, + 'then' and 'else' are sub-regexes, and 'else' is optional. */ + GET_ARG; + if (arg >= groups) + FAIL; + GET_SKIP; + code--; /* The skip is relative to the first arg! */ + /* There are two possibilities here: if there is both a 'then' + part and an 'else' part, the generated code looks like: + + GROUPREF_EXISTS + + + ...then part... + JUMP + + ( jumps here) + ...else part... + ( jumps here) + + If there is only a 'then' part, it looks like: + + GROUPREF_EXISTS + + + ...then part... + ( jumps here) + + There is no direct way to decide which it is, and we don't want + to allow arbitrary jumps anywhere in the code; so we just look + for a JUMP opcode preceding our skip target. + */ + if (skip >= 3 && code+skip-3 >= code && + code[skip-3] == SRE_OP_JUMP) + { + VTRACE(("both then and else parts present\n")); + if (!_validate_inner(code+1, code+skip-3, groups)) + FAIL; + code += skip-2; /* Position after JUMP, at */ + GET_SKIP; + if (!_validate_inner(code, code+skip-1, groups)) + FAIL; + code += skip-1; + } + else { + VTRACE(("only a then part present\n")); + if (!_validate_inner(code+1, code+skip-1, groups)) + FAIL; + code += skip-1; + } + break; + + case SRE_OP_ASSERT: + case SRE_OP_ASSERT_NOT: + GET_SKIP; + GET_ARG; /* 0 for lookahead, width for lookbehind */ + code--; /* Back up over arg to simplify math below */ + if (arg & 0x80000000) + FAIL; /* Width too large */ + /* Stop 1 before the end; we check the SUCCESS below */ + if (!_validate_inner(code+1, code+skip-2, groups)) + FAIL; + code += skip-2; + GET_OP; + if (op != SRE_OP_SUCCESS) + FAIL; + break; + + default: + FAIL; + + } + } + + VTRACE(("okay\n")); + return 1; +} + +static int +_validate_outer(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups) +{ + if (groups < 0 || groups > 100 || code >= end || end[-1] != SRE_OP_SUCCESS) + FAIL; + if (groups == 0) /* fix for simplejson */ + groups = 100; /* 100 groups should always be safe */ + return _validate_inner(code, end-1, groups); +} + +static int +_validate(PatternObject *self) +{ + if (!_validate_outer(self->code, self->code+self->codesize, self->groups)) + { + PyErr_SetString(PyExc_RuntimeError, "invalid SRE code"); + return 0; + } + else + VTRACE(("Success!\n")); + return 1; +} + +/* -------------------------------------------------------------------- */ /* match methods */ static void From buildbot at python.org Tue Aug 5 06:25:07 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 04:25:07 +0000 Subject: [Python-checkins] buildbot failure in x86 gentoo trunk Message-ID: <20080805042507.7ED551E400F@bag.python.org> The Buildbot has detected a new failure of x86 gentoo trunk. Full details are available at: http://www.python.org/dev/buildbot/all/x86%20gentoo%20trunk/builds/4032 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: norwitz-x86 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum BUILD FAILED: failed test Excerpt from the test logfile: sincerely, -The Buildbot From buildbot at python.org Tue Aug 5 06:37:47 2008 From: buildbot at python.org (buildbot at python.org) Date: Tue, 05 Aug 2008 04:37:47 +0000 Subject: [Python-checkins] buildbot failure in amd64 XP trunk Message-ID: <20080805043747.AF9721E4008@bag.python.org> The Buildbot has detected a new failure of amd64 XP trunk. Full details are available at: http://www.python.org/dev/buildbot/all/amd64%20XP%20trunk/builds/122 Buildbot URL: http://www.python.org/dev/buildbot/all/ Buildslave for this Build: heller-windows-amd64 Build Reason: Build Source Stamp: [branch trunk] HEAD Blamelist: guido.van.rossum BUILD FAILED: failed failed slave lost sincerely, -The Buildbot From grm4000 at gmail.com Mon Aug 4 14:50:42 2008 From: grm4000 at gmail.com (Professional Grammar Course) Date: Mon, 4 Aug 2008 20:50:42 +0800 Subject: [Python-checkins] Perfecting Your Grammar Course 18 to 20 August Message-ID: <20080804125042128.618327FC60577D58@C34342-49909> If you can't read this, please visit our website at: http://www.trilifecom.com. Should you wish to enquire about our courses, change your email address or unsubscribe, please email us at courses at trilifecom.com. We apologise if you have unsubscribed and are still receiving emails from us. We need the exact email address to unsubscribe. Thanks. Take a minute to read these sentences: 'I'm in the train; I'll be at work soon.' 'Everyone of us are ready for the performance appraisal.' 'Let me bring you to see the Manager.' 'Wanted: Part-time female Receptionist.' 'Peter, as well as other staff, have arrived for the meeting.' If you think these sentences are correct, you'll probably need our 3-day comprehensive, 'Perfecting Your Grammar' workshop to be held on 18 to 20 August. Please register by 11 August. This workshop covers all the most important aspects of grammar that we need, in order to be effective speakers and writers at work, and in every communication situation in our lives. Are you curious about the corrections to the sentences? Click here for the corrections. (The fee for the course is $688/-, no GST.) For full course and registration details, please visit our website at: http://www.trilifecom.com. Please also visit our website for information on our other courses. We look forward with great pleasure to welcoming you to our course. Yours sincerely Merle Celine Magness (Ms) TriLife Communications, Singapore Tel: 6581-0970 Email: courses at trilifecom.com website: http://www.trilifecom.com This is a genuine advertisement. Should you wish to enquire about our courses, change your email address or unsubscribe, please email us at courses at trilifecom.com. We apologise if you have unsubscribed and are still receiving emails from us. We need the exact email address to unsubscribe. -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Tue Aug 5 11:55:20 2008 From: python-checkins at python.org (georg.brandl) Date: Tue, 5 Aug 2008 11:55:20 +0200 (CEST) Subject: [Python-checkins] r65546 - in doctools/branches/0.4.x/sphinx: static/searchtools.js templates/search.html Message-ID: <20080805095520.D81AB1E4009@bag.python.org> Author: georg.brandl Date: Tue Aug 5 11:55:20 2008 New Revision: 65546 Log: Show an indication while downloading the search index. Modified: doctools/branches/0.4.x/sphinx/static/searchtools.js doctools/branches/0.4.x/sphinx/templates/search.html Modified: doctools/branches/0.4.x/sphinx/static/searchtools.js ============================================================================== --- doctools/branches/0.4.x/sphinx/static/searchtools.js (original) +++ doctools/branches/0.4.x/sphinx/static/searchtools.js Tue Aug 5 11:55:20 2008 @@ -243,6 +243,7 @@ var dots = $('').appendTo(title); var status = $('

    ').appendTo(out); var output = $('