From 22768184cbbaa4bd6083c51c28183be7f4fc3d69 Mon Sep 17 00:00:00 2001 From: cvs2svn Date: Fri, 28 Dec 2001 04:27:46 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create branch 'release22-maint'. --- Demo/sgi/video/vcopy.py | 134 -- Demo/sgi/video/vinfo.py | 90 -- Demo/sgi/video/vtime.py | 106 -- Doc/Makefile | 2 +- Doc/texinputs/boilerplate.tex | 4 +- Doc/whatsnew/whatsnew20.tex | 1335 ------------------- Doc/whatsnew/whatsnew21.tex | 868 ------------- Include/patchlevel.h | 6 +- Lib/compiler/ast.py | 15 + Lib/compiler/pycodegen.py | 12 +- Lib/compiler/symbols.py | 2 + Lib/compiler/transformer.py | 2 +- Lib/dumbdbm.py | 6 + Lib/idlelib/AutoExpand.py | 91 -- Lib/idlelib/AutoIndent.py | 554 -------- Lib/idlelib/Bindings.py | 76 -- Lib/idlelib/CREDITS.txt | 17 - Lib/idlelib/CallTipWindow.py | 71 - Lib/idlelib/CallTips.py | 190 --- Lib/idlelib/ChangeLog | 1587 ----------------------- Lib/idlelib/ClassBrowser.py | 224 ---- Lib/idlelib/ColorDelegator.py | 249 ---- Lib/idlelib/Debugger.py | 308 ----- Lib/idlelib/Delegator.py | 33 - Lib/idlelib/EditorWindow.py | 735 ----------- Lib/idlelib/ExecBinding.py | 198 --- Lib/idlelib/FileList.py | 146 --- Lib/idlelib/FormatParagraph.py | 155 --- Lib/idlelib/FrameViewer.py | 38 - Lib/idlelib/GrepDialog.py | 135 -- Lib/idlelib/INSTALL.txt | 58 - Lib/idlelib/IOBinding.py | 254 ---- Lib/idlelib/Icons/folder.gif | Bin 120 -> 0 bytes Lib/idlelib/Icons/minusnode.gif | Bin 96 -> 0 bytes Lib/idlelib/Icons/openfolder.gif | Bin 125 -> 0 bytes Lib/idlelib/Icons/plusnode.gif | Bin 79 -> 0 bytes Lib/idlelib/Icons/python.gif | Bin 125 -> 0 bytes Lib/idlelib/Icons/tk.gif | Bin 85 -> 0 bytes Lib/idlelib/IdleConf.py | 113 -- Lib/idlelib/IdleHistory.py | 88 -- Lib/idlelib/LICENSE.txt | 50 - Lib/idlelib/MultiScrolledLists.py | 138 -- Lib/idlelib/MultiStatusBar.py | 32 - Lib/idlelib/NEWS.txt | 173 --- Lib/idlelib/ObjectBrowser.py | 151 --- Lib/idlelib/OldStackViewer.py | 276 ---- Lib/idlelib/OutputWindow.py | 279 ---- Lib/idlelib/ParenMatch.py | 191 --- Lib/idlelib/PathBrowser.py | 95 -- Lib/idlelib/Percolator.py | 85 -- Lib/idlelib/PyParse.py | 589 --------- Lib/idlelib/PyShell.py | 903 ------------- Lib/idlelib/README.txt | 158 --- Lib/idlelib/Remote.py | 101 -- Lib/idlelib/RemoteInterp.py | 342 ----- Lib/idlelib/ReplaceDialog.py | 188 --- Lib/idlelib/ScriptBinding.py | 173 --- Lib/idlelib/ScrolledList.py | 139 -- Lib/idlelib/SearchBinding.py | 97 -- Lib/idlelib/SearchDialog.py | 67 - Lib/idlelib/SearchDialogBase.py | 129 -- Lib/idlelib/SearchEngine.py | 221 ---- Lib/idlelib/Separator.py | 92 -- Lib/idlelib/StackViewer.py | 147 --- Lib/idlelib/TODO.txt | 212 --- Lib/idlelib/ToolTip.py | 87 -- Lib/idlelib/TreeWidget.py | 471 ------- Lib/idlelib/UndoDelegator.py | 352 ----- Lib/idlelib/WidgetRedirector.py | 92 -- Lib/idlelib/WindowList.py | 85 -- Lib/idlelib/ZoomHeight.py | 46 - Lib/idlelib/__init__.py | 1 - Lib/idlelib/aboutDialog.py | 135 -- Lib/idlelib/config-extensions.def | 34 - Lib/idlelib/config-highlight.def | 56 - Lib/idlelib/config-keys.def | 64 - Lib/idlelib/config-main.def | 79 -- Lib/idlelib/config-unix.txt | 3 - Lib/idlelib/config-win.txt | 3 - Lib/idlelib/config.txt | 66 - Lib/idlelib/configDialog.py | 623 --------- Lib/idlelib/configHandler.py | 254 ---- Lib/idlelib/dynOptionMenuWidget.py | 41 - Lib/idlelib/eventparse.py | 93 -- Lib/idlelib/extend.txt | 120 -- Lib/idlelib/help.txt | 165 --- Lib/idlelib/idle | 4 - Lib/idlelib/idle.bat | 3 - Lib/idlelib/idle.py | 4 - Lib/idlelib/idle.pyw | 12 - Lib/idlelib/idlever.py | 1 - Lib/idlelib/keydefs.py | 55 - Lib/idlelib/loader.py | 64 - Lib/idlelib/protocol.py | 369 ------ Lib/idlelib/setup.py | 86 -- Lib/idlelib/spawn.py | 58 - Lib/idlelib/tabpage.py | 110 -- Lib/idlelib/testcode.py | 31 - Lib/idlelib/textView.py | 77 -- Lib/test/test_cpickle.py | 7 + Mac/Build/PythonCore.mcp | Bin 201534 -> 201534 bytes Mac/Distributions/(vise)/Python 2.2.vct | Bin 673637 -> 674056 bytes Mac/Distributions/binary.exclude | 1 + Mac/Distributions/binary.include | 4 +- Mac/Distributions/dev.exclude | 1 + Mac/Distributions/dev.include | 26 +- Mac/Include/macbuildno.h | 2 +- Mac/Python/macglue.c | 11 + Mac/ReadMe | 45 +- Mac/Relnotes | 24 +- Mac/Unsupported/mactcp/dnrglue.c | 301 ----- Mac/_checkversion.py | 2 +- Misc/NEWS | 41 + Modules/cPickle.c | 8 +- Python/mysnprintf.c | 4 +- 115 files changed, 155 insertions(+), 16996 deletions(-) delete mode 100755 Demo/sgi/video/vcopy.py delete mode 100755 Demo/sgi/video/vinfo.py delete mode 100755 Demo/sgi/video/vtime.py delete mode 100644 Doc/whatsnew/whatsnew20.tex delete mode 100644 Doc/whatsnew/whatsnew21.tex delete mode 100644 Lib/idlelib/AutoExpand.py delete mode 100644 Lib/idlelib/AutoIndent.py delete mode 100644 Lib/idlelib/Bindings.py delete mode 100644 Lib/idlelib/CREDITS.txt delete mode 100644 Lib/idlelib/CallTipWindow.py delete mode 100644 Lib/idlelib/CallTips.py delete mode 100644 Lib/idlelib/ChangeLog delete mode 100644 Lib/idlelib/ClassBrowser.py delete mode 100644 Lib/idlelib/ColorDelegator.py delete mode 100644 Lib/idlelib/Debugger.py delete mode 100644 Lib/idlelib/Delegator.py delete mode 100644 Lib/idlelib/EditorWindow.py delete mode 100644 Lib/idlelib/ExecBinding.py delete mode 100644 Lib/idlelib/FileList.py delete mode 100644 Lib/idlelib/FormatParagraph.py delete mode 100644 Lib/idlelib/FrameViewer.py delete mode 100644 Lib/idlelib/GrepDialog.py delete mode 100644 Lib/idlelib/INSTALL.txt delete mode 100644 Lib/idlelib/IOBinding.py delete mode 100644 Lib/idlelib/Icons/folder.gif delete mode 100644 Lib/idlelib/Icons/minusnode.gif delete mode 100644 Lib/idlelib/Icons/openfolder.gif delete mode 100644 Lib/idlelib/Icons/plusnode.gif delete mode 100644 Lib/idlelib/Icons/python.gif delete mode 100644 Lib/idlelib/Icons/tk.gif delete mode 100644 Lib/idlelib/IdleConf.py delete mode 100644 Lib/idlelib/IdleHistory.py delete mode 100644 Lib/idlelib/LICENSE.txt delete mode 100644 Lib/idlelib/MultiScrolledLists.py delete mode 100644 Lib/idlelib/MultiStatusBar.py delete mode 100644 Lib/idlelib/NEWS.txt delete mode 100644 Lib/idlelib/ObjectBrowser.py delete mode 100644 Lib/idlelib/OldStackViewer.py delete mode 100644 Lib/idlelib/OutputWindow.py delete mode 100644 Lib/idlelib/ParenMatch.py delete mode 100644 Lib/idlelib/PathBrowser.py delete mode 100644 Lib/idlelib/Percolator.py delete mode 100644 Lib/idlelib/PyParse.py delete mode 100644 Lib/idlelib/PyShell.py delete mode 100644 Lib/idlelib/README.txt delete mode 100644 Lib/idlelib/Remote.py delete mode 100644 Lib/idlelib/RemoteInterp.py delete mode 100644 Lib/idlelib/ReplaceDialog.py delete mode 100644 Lib/idlelib/ScriptBinding.py delete mode 100644 Lib/idlelib/ScrolledList.py delete mode 100644 Lib/idlelib/SearchBinding.py delete mode 100644 Lib/idlelib/SearchDialog.py delete mode 100644 Lib/idlelib/SearchDialogBase.py delete mode 100644 Lib/idlelib/SearchEngine.py delete mode 100644 Lib/idlelib/Separator.py delete mode 100644 Lib/idlelib/StackViewer.py delete mode 100644 Lib/idlelib/TODO.txt delete mode 100644 Lib/idlelib/ToolTip.py delete mode 100644 Lib/idlelib/TreeWidget.py delete mode 100644 Lib/idlelib/UndoDelegator.py delete mode 100644 Lib/idlelib/WidgetRedirector.py delete mode 100644 Lib/idlelib/WindowList.py delete mode 100644 Lib/idlelib/ZoomHeight.py delete mode 100644 Lib/idlelib/__init__.py delete mode 100644 Lib/idlelib/aboutDialog.py delete mode 100644 Lib/idlelib/config-extensions.def delete mode 100644 Lib/idlelib/config-highlight.def delete mode 100644 Lib/idlelib/config-keys.def delete mode 100644 Lib/idlelib/config-main.def delete mode 100644 Lib/idlelib/config-unix.txt delete mode 100644 Lib/idlelib/config-win.txt delete mode 100644 Lib/idlelib/config.txt delete mode 100644 Lib/idlelib/configDialog.py delete mode 100644 Lib/idlelib/configHandler.py delete mode 100644 Lib/idlelib/dynOptionMenuWidget.py delete mode 100644 Lib/idlelib/eventparse.py delete mode 100644 Lib/idlelib/extend.txt delete mode 100644 Lib/idlelib/help.txt delete mode 100755 Lib/idlelib/idle delete mode 100755 Lib/idlelib/idle.bat delete mode 100644 Lib/idlelib/idle.py delete mode 100644 Lib/idlelib/idle.pyw delete mode 100644 Lib/idlelib/idlever.py delete mode 100644 Lib/idlelib/keydefs.py delete mode 100644 Lib/idlelib/loader.py delete mode 100644 Lib/idlelib/protocol.py delete mode 100644 Lib/idlelib/setup.py delete mode 100644 Lib/idlelib/spawn.py delete mode 100644 Lib/idlelib/tabpage.py delete mode 100644 Lib/idlelib/testcode.py delete mode 100644 Lib/idlelib/textView.py delete mode 100644 Mac/Unsupported/mactcp/dnrglue.c diff --git a/Demo/sgi/video/vcopy.py b/Demo/sgi/video/vcopy.py deleted file mode 100755 index d32bc1f8019f..000000000000 --- a/Demo/sgi/video/vcopy.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copy a video file, interactively, frame-by-frame. - -import sys -import getopt -from gl import * -from DEVICE import * -import VFile -import string -import imageop - -def report(time, iframe): - print 'Frame', iframe, ': t =', time - -def usage(): - sys.stderr.write('usage: vcopy [-t type] [-m treshold] [-a] infile outfile\n') - sys.stderr.write('-t Convert to other type\n') - sys.stderr.write('-a Automatic\n') - sys.stderr.write('-m Convert grey to mono with treshold\n') - sys.stderr.write('-d Convert grey to mono with dithering\n') - sys.exit(2) - -def help(): - print 'Command summary:' - print 'n get next image from input' - print 'w write current image to output' - -def main(): - foreground() - opts, args = getopt.getopt(sys.argv[1:], 't:am:d') - if len(args) <> 2: - usage() - [ifile, ofile] = args - print 'open film ', ifile - ifilm = VFile.VinFile().init(ifile) - print 'open output ', ofile - ofilm = VFile.VoutFile().init(ofile) - - ofilm.setinfo(ifilm.getinfo()) - - use_grabber = 0 - continuous = 0 - tomono = 0 - tomonodither = 0 - for o, a in opts: - if o == '-t': - ofilm.format = a - use_grabber = 1 - if o == '-a': - continuous = 1 - if o == '-m': - if ifilm.format <> 'grey': - print '-m only supported for greyscale' - sys.exit(1) - tomono = 1 - treshold = string.atoi(a) - ofilm.format = 'mono' - if o == '-d': - if ifilm.format <> 'grey': - print '-m only supported for greyscale' - sys.exit(1) - tomonodither = 1 - ofilm.format = 'mono' - - ofilm.writeheader() - # - prefsize(ifilm.width, ifilm.height) - w = winopen(ifile) - qdevice(KEYBD) - qdevice(ESCKEY) - qdevice(WINQUIT) - qdevice(WINSHUT) - print 'qdevice calls done' - # - help() - # - time, data, cdata = ifilm.getnextframe() - ifilm.showframe(data, cdata) - iframe = 1 - report(time, iframe) - # - while 1: - if continuous: - dev = KEYBD - else: - dev, val = qread() - if dev in (ESCKEY, WINQUIT, WINSHUT): - break - if dev == REDRAW: - reshapeviewport() - elif dev == KEYBD: - if continuous: - c = '0' - else: - c = chr(val) - #XXX Debug - if c == 'R': - c3i(255,0,0) - clear() - if c == 'G': - c3i(0,255,0) - clear() - if c == 'B': - c3i(0,0,255) - clear() - if c == 'w' or continuous: - if use_grabber: - data, cdata = ofilm.grabframe() - if tomono: - data = imageop.grey2mono(data, \ - ifilm.width, ifilm.height, \ - treshold) - if tomonodither: - data = imageop.dither2mono(data, \ - ifilm.width, ifilm.height) - ofilm.writeframe(time, data, cdata) - print 'Frame', iframe, 'written.' - if c == 'n' or continuous: - try: - time,data,cdata = ifilm.getnextframe() - ifilm.showframe(data, cdata) - iframe = iframe+1 - report(time, iframe) - except EOFError: - print 'EOF' - if continuous: - break - ringbell() - elif dev == INPUTCHANGE: - pass - else: - print '(dev, val) =', (dev, val) - ofilm.close() - -main() diff --git a/Demo/sgi/video/vinfo.py b/Demo/sgi/video/vinfo.py deleted file mode 100755 index 7f98237a9743..000000000000 --- a/Demo/sgi/video/vinfo.py +++ /dev/null @@ -1,90 +0,0 @@ -from gl import * -from GL import * -from DEVICE import * -import time -import sys -import getopt - -class Struct(): pass -epoch = Struct() -EndOfFile = 'End of file' -bye = 'bye' - -def openvideo(filename): - f = open(filename, 'r') - line = f.readline() - if not line: raise EndOfFile - if line[:4] == 'CMIF': line = f.readline() - x = eval(line[:-1]) - if len(x) == 3: w, h, pf = x - else: w, h = x; pf = 2 - return f, w, h, pf - -def loadframe(f, w, h, pf): - line = f.readline() - if line == '': - raise EndOfFile - x = eval(line[:-1]) - if type(x) == type(0) or type(x) == type(0.0): - tijd = x - if pf == 0: - size = w*h*4 - else: - size = (w/pf) * (h/pf) - else: - tijd, size = x - f.seek(size, 1) - return tijd - -def main(): - delta = 0 - short = 0 - try: - opts, names = getopt.getopt(sys.argv[1:], 'ds') - except getopt.error, msg: - sys.stderr.write(msg + '\n') - sys.stderr.write('usage: vinfo [-d] [-s] [file] ...\n') - sys.exit(2) - for opt, arg in opts: - if opt == '-d': delta = 1 # print delta between frames - elif opt == '-s': short = 1 # short: don't print times - if names == []: - names = ['film.video'] - for name in names: - try: - f, w, h, pf = openvideo(name) - except: - sys.stderr.write(name + ': cannot open\n') - continue - if pf == 0: - size = w*h*4 - else: - size = (w/pf) * (h/pf) - print name, ':', w, 'x', h, '; pf =', pf, ', size =', size, - if pf == 0: - print '(color)', - else: - print '(' + `(w/pf)` + 'x' + `(h/pf)` + ')', - if (w/pf)%4 <> 0: print '!!!', - print - num = 0 - try: - otijd = 0 - while not short: - try: - tijd = loadframe(f, w, h, pf) - if delta: print '\t' + `tijd-otijd`, - else: print '\t' + `tijd`, - otijd = tijd - num = num + 1 - if num % 8 == 0: - print - except EndOfFile: - raise bye - except bye: - pass - if num % 8 <> 0: - print - f.close() - -main() diff --git a/Demo/sgi/video/vtime.py b/Demo/sgi/video/vtime.py deleted file mode 100755 index c333e57983ec..000000000000 --- a/Demo/sgi/video/vtime.py +++ /dev/null @@ -1,106 +0,0 @@ -# -# Module vtime - Keep virtual time between two nodes. -# -# We try for synchronised clocks by sending a packet of the for -# (1,mytime,0) to the other side, and waiting (at most) a second for -# a reply. This reply has the form (2,mytime,histime), and we can -# estimate the time difference by defining histime to be exactly half-way -# between the time we sent our message and got our reply. We send a -# final (3,mynewtime,histime) message to allow the other side to do the -# same computations. -# -# Note that the protocol suffers heavily from the 2-army problem. -# It'll have to do until I can read up on time-sync protocols, though. -# -from socket import * -import time - -MSGSIZE = 100 -MSGTIMEOUT = 1000 - -recv_timeout = 'receive timeout' -bad_connect = 'Bad connection' - -def timeavg(a,b): - return int((long(a)+b)/2L) -def tryrecv(s): - cnt = 0 - while 1: - if s.avail(): - return s.recvfrom(MSGSIZE) - time.millisleep(100) - cnt = cnt + 100 - if cnt > MSGTIMEOUT: - raise recv_timeout - -class VTime(): - def init(self,(client,host,port)): - s = socket(AF_INET, SOCK_DGRAM) - host = gethostbyname(host) - localhost = gethostbyname(gethostname()) - raddr = (host,port) - s.bind((localhost,port)) - if client: - # - # We loop here because we want the *second* measurement - # for accuracy - for loopct in (0,2): - curtijd = time.millitimer() - check = `(loopct,curtijd,0)` - s.sendto(check,raddr) - while 1: - try: - if loopct: - data, other = s.recvfrom(MSGSIZE) - else: - data, other = tryrecv(s) - newtijd = time.millitimer() - if other <> raddr: - print 'Someone else syncing to us: ', other - raise bad_connect - data = eval(data) - if data[:2] == (loopct+1,curtijd): - break - if data[0] <> 2: - print 'Illegal sync reply: ', data - raise bad_connect - except recv_timeout: - curtijd = time.millitimer() - check = `(loopct,curtijd,0)` - s.sendto(check,raddr) - histime = data[2] - s.sendto(`(4,newtijd,histime)`,raddr) - mytime = timeavg(curtijd,newtijd) - #mytime = curtijd - self.timediff = histime - mytime - else: - while 1: - data,other = s.recvfrom(MSGSIZE) - if other <> raddr: - print 'Someone else syncing to us: ', other, ' Wanted ', raddr - raise bad_connect - data = eval(data) - if data[0] in (0,2): - curtijd = time.millitimer() - s.sendto(`(data[0]+1,data[1],curtijd)`,raddr) - elif data[0] == 4: - newtijd = time.millitimer() - histime = data[1] - mytime = timeavg(curtijd,newtijd) - #mytime = curtijd - self.timediff = histime-mytime - break - else: - print 'Funny data: ', data - raise bad_connect - return self - # - def his2mine(self,tijd): - return tijd - self.timediff - # - def mine2his(self, tijd): - return tijd + self.timediff - -def test(clt, host, port): - xx = VTime().init(clt,host,port) - print 'Time diff: ', xx.his2mine(0) diff --git a/Doc/Makefile b/Doc/Makefile index 5911fd02d78f..dbdc98a0f88f 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -66,7 +66,7 @@ TOOLSDIR= tools # This is the *documentation* release, and is used to construct the file # names of the downloadable tarballs. -RELEASE=2.2c1+ +RELEASE=2.2 PYTHON= python DVIPS= dvips -N0 -t $(PAPER) diff --git a/Doc/texinputs/boilerplate.tex b/Doc/texinputs/boilerplate.tex index 97ee49e85fa9..9107e54ad332 100644 --- a/Doc/texinputs/boilerplate.tex +++ b/Doc/texinputs/boilerplate.tex @@ -5,7 +5,7 @@ Email: \email{python-docs@python.org} } -\date{\today} % XXX update before release! +\date{December 21, 2001} % XXX update before release! \release{2.2} % software release, not documentation -\setreleaseinfo{c1+} % empty for final release +\setreleaseinfo{} % empty for final release \setshortversion{2.2} % major.minor only for software diff --git a/Doc/whatsnew/whatsnew20.tex b/Doc/whatsnew/whatsnew20.tex deleted file mode 100644 index 229f9ee471f4..000000000000 --- a/Doc/whatsnew/whatsnew20.tex +++ /dev/null @@ -1,1335 +0,0 @@ -\documentclass{howto} - -% $Id$ - -\title{What's New in Python 2.0} -\release{1.01} -\author{A.M. Kuchling and Moshe Zadka} -\authoraddress{\email{akuchlin@mems-exchange.org}, \email{moshez@math.huji.ac.il} } -\begin{document} -\maketitle\tableofcontents - -\section{Introduction} - -A new release of Python, version 2.0, will be released some time this -autumn. Beta versions are already available from -\url{http://www.pythonlabs.com/products/python2.0/}. This article -covers the exciting new features in 2.0, highlights some other useful -changes, and points out a few incompatible changes that may require -rewriting code. - -Python's development never completely stops between releases, and a -steady flow of bug fixes and improvements are always being submitted. -A host of minor fixes, a few optimizations, additional docstrings, and -better error messages went into 2.0; to list them all would be -impossible, but they're certainly significant. Consult the -publicly-available CVS logs if you want to see the full list. This -progress is due to the five developers working for -PythonLabs are now getting paid to spend their days fixing bugs, -and also due to the improved communication resulting -from moving to SourceForge. - -% ====================================================================== -\section{What About Python 1.6?} - -Python 1.6 can be thought of as the Contractual Obligations Python -release. After the core development team left CNRI in May 2000, CNRI -requested that a 1.6 release be created, containing all the work on -Python that had been performed at CNRI. Python 1.6 therefore -represents the state of the CVS tree as of May 2000, with the most -significant new feature being Unicode support. Development continued -after May, of course, so the 1.6 tree received a few fixes to ensure -that it's forward-compatible with Python 2.0. 1.6 is therefore part -of Python's evolution, and not a side branch. - -So, should you take much interest in Python 1.6? Probably not. The -1.6final and 2.0beta1 releases were made on the same day (September 5, -2000), the plan being to finalize Python 2.0 within a month or so. If -you have applications to maintain, there seems little point in -breaking things by moving to 1.6, fixing them, and then having another -round of breakage within a month by moving to 2.0; you're better off -just going straight to 2.0. Most of the really interesting features -described in this document are only in 2.0, because a lot of work was -done between May and September. - -% ====================================================================== -\section{New Development Process} - -The most important change in Python 2.0 may not be to the code at all, -but to how Python is developed: in May 2000 the Python developers -began using the tools made available by SourceForge for storing -source code, tracking bug reports, and managing the queue of patch -submissions. To report bugs or submit patches for Python 2.0, use the -bug tracking and patch manager tools available from Python's project -page, located at \url{http://sourceforge.net/projects/python/}. - -The most important of the services now hosted at SourceForge is the -Python CVS tree, the version-controlled repository containing the -source code for Python. Previously, there were roughly 7 or so people -who had write access to the CVS tree, and all patches had to be -inspected and checked in by one of the people on this short list. -Obviously, this wasn't very scalable. By moving the CVS tree to -SourceForge, it became possible to grant write access to more people; -as of September 2000 there were 27 people able to check in changes, a -fourfold increase. This makes possible large-scale changes that -wouldn't be attempted if they'd have to be filtered through the small -group of core developers. For example, one day Peter Schneider-Kamp -took it into his head to drop K\&R C compatibility and convert the C -source for Python to ANSI C. After getting approval on the python-dev -mailing list, he launched into a flurry of checkins that lasted about -a week, other developers joined in to help, and the job was done. If -there were only 5 people with write access, probably that task would -have been viewed as ``nice, but not worth the time and effort needed'' -and it would never have gotten done. - -The shift to using SourceForge's services has resulted in a remarkable -increase in the speed of development. Patches now get submitted, -commented on, revised by people other than the original submitter, and -bounced back and forth between people until the patch is deemed worth -checking in. Bugs are tracked in one central location and can be -assigned to a specific person for fixing, and we can count the number -of open bugs to measure progress. This didn't come without a cost: -developers now have more e-mail to deal with, more mailing lists to -follow, and special tools had to be written for the new environment. -For example, SourceForge sends default patch and bug notification -e-mail messages that are completely unhelpful, so Ka-Ping Yee wrote an -HTML screen-scraper that sends more useful messages. - -The ease of adding code caused a few initial growing pains, such as -code was checked in before it was ready or without getting clear -agreement from the developer group. The approval process that has -emerged is somewhat similar to that used by the Apache group. -Developers can vote +1, +0, -0, or -1 on a patch; +1 and -1 denote -acceptance or rejection, while +0 and -0 mean the developer is mostly -indifferent to the change, though with a slight positive or negative -slant. The most significant change from the Apache model is that the -voting is essentially advisory, letting Guido van Rossum, who has -Benevolent Dictator For Life status, know what the general opinion is. -He can still ignore the result of a vote, and approve or -reject a change even if the community disagrees with him. - -Producing an actual patch is the last step in adding a new feature, -and is usually easy compared to the earlier task of coming up with a -good design. Discussions of new features can often explode into -lengthy mailing list threads, making the discussion hard to follow, -and no one can read every posting to python-dev. Therefore, a -relatively formal process has been set up to write Python Enhancement -Proposals (PEPs), modelled on the Internet RFC process. PEPs are -draft documents that describe a proposed new feature, and are -continually revised until the community reaches a consensus, either -accepting or rejecting the proposal. Quoting from the introduction to -PEP 1, ``PEP Purpose and Guidelines'': - -\begin{quotation} - PEP stands for Python Enhancement Proposal. A PEP is a design - document providing information to the Python community, or - describing a new feature for Python. The PEP should provide a - concise technical specification of the feature and a rationale for - the feature. - - We intend PEPs to be the primary mechanisms for proposing new - features, for collecting community input on an issue, and for - documenting the design decisions that have gone into Python. The - PEP author is responsible for building consensus within the - community and documenting dissenting opinions. -\end{quotation} - -Read the rest of PEP 1 for the details of the PEP editorial process, -style, and format. PEPs are kept in the Python CVS tree on -SourceForge, though they're not part of the Python 2.0 distribution, -and are also available in HTML form from -\url{http://python.sourceforge.net/peps/}. As of September 2000, -there are 25 PEPS, ranging from PEP 201, ``Lockstep Iteration'', to -PEP 225, ``Elementwise/Objectwise Operators''. - -% ====================================================================== -\section{Unicode} - -The largest new feature in Python 2.0 is a new fundamental data type: -Unicode strings. Unicode uses 16-bit numbers to represent characters -instead of the 8-bit number used by ASCII, meaning that 65,536 -distinct characters can be supported. - -The final interface for Unicode support was arrived at through -countless often-stormy discussions on the python-dev mailing list, and -mostly implemented by Marc-Andr\'e Lemburg, based on a Unicode string -type implementation by Fredrik Lundh. A detailed explanation of the -interface is in the file \file{Misc/unicode.txt} in the Python source -distribution; it's also available on the Web at -\url{http://starship.python.net/crew/lemburg/unicode-proposal.txt}. -This article will simply cover the most significant points about the Unicode -interfaces. - -In Python source code, Unicode strings are written as -\code{u"string"}. Arbitrary Unicode characters can be written using a -new escape sequence, \code{\e u\var{HHHH}}, where \var{HHHH} is a -4-digit hexadecimal number from 0000 to FFFF. The existing -\code{\e x\var{HHHH}} escape sequence can also be used, and octal -escapes can be used for characters up to U+01FF, which is represented -by \code{\e 777}. - -Unicode strings, just like regular strings, are an immutable sequence -type. They can be indexed and sliced, but not modified in place. -Unicode strings have an \method{encode( \optional{encoding} )} method -that returns an 8-bit string in the desired encoding. Encodings are -named by strings, such as \code{'ascii'}, \code{'utf-8'}, -\code{'iso-8859-1'}, or whatever. A codec API is defined for -implementing and registering new encodings that are then available -throughout a Python program. If an encoding isn't specified, the -default encoding is usually 7-bit ASCII, though it can be changed for -your Python installation by calling the -\function{sys.setdefaultencoding(\var{encoding})} function in a -customised version of \file{site.py}. - -Combining 8-bit and Unicode strings always coerces to Unicode, using -the default ASCII encoding; the result of \code{'a' + u'bc'} is -\code{u'abc'}. - -New built-in functions have been added, and existing built-ins -modified to support Unicode: - -\begin{itemize} -\item \code{unichr(\var{ch})} returns a Unicode string 1 character -long, containing the character \var{ch}. - -\item \code{ord(\var{u})}, where \var{u} is a 1-character regular or Unicode string, returns the number of the character as an integer. - -\item \code{unicode(\var{string} \optional{, \var{encoding}} -\optional{, \var{errors}} ) } creates a Unicode string from an 8-bit -string. \code{encoding} is a string naming the encoding to use. -The \code{errors} parameter specifies the treatment of characters that -are invalid for the current encoding; passing \code{'strict'} as the -value causes an exception to be raised on any encoding error, while -\code{'ignore'} causes errors to be silently ignored and -\code{'replace'} uses U+FFFD, the official replacement character, in -case of any problems. - -\item The \keyword{exec} statement, and various built-ins such as -\code{eval()}, \code{getattr()}, and \code{setattr()} will also -accept Unicode strings as well as regular strings. (It's possible -that the process of fixing this missed some built-ins; if you find a -built-in function that accepts strings but doesn't accept Unicode -strings at all, please report it as a bug.) - -\end{itemize} - -A new module, \module{unicodedata}, provides an interface to Unicode -character properties. For example, \code{unicodedata.category(u'A')} -returns the 2-character string 'Lu', the 'L' denoting it's a letter, -and 'u' meaning that it's uppercase. -\code{u.bidirectional(u'\e x0660')} returns 'AN', meaning that U+0660 is -an Arabic number. - -The \module{codecs} module contains functions to look up existing encodings -and register new ones. Unless you want to implement a -new encoding, you'll most often use the -\function{codecs.lookup(\var{encoding})} function, which returns a -4-element tuple: \code{(\var{encode_func}, -\var{decode_func}, \var{stream_reader}, \var{stream_writer})}. - -\begin{itemize} -\item \var{encode_func} is a function that takes a Unicode string, and -returns a 2-tuple \code{(\var{string}, \var{length})}. \var{string} -is an 8-bit string containing a portion (perhaps all) of the Unicode -string converted into the given encoding, and \var{length} tells you -how much of the Unicode string was converted. - -\item \var{decode_func} is the opposite of \var{encode_func}, taking -an 8-bit string and returning a 2-tuple \code{(\var{ustring}, -\var{length})}, consisting of the resulting Unicode string -\var{ustring} and the integer \var{length} telling how much of the -8-bit string was consumed. - -\item \var{stream_reader} is a class that supports decoding input from -a stream. \var{stream_reader(\var{file_obj})} returns an object that -supports the \method{read()}, \method{readline()}, and -\method{readlines()} methods. These methods will all translate from -the given encoding and return Unicode strings. - -\item \var{stream_writer}, similarly, is a class that supports -encoding output to a stream. \var{stream_writer(\var{file_obj})} -returns an object that supports the \method{write()} and -\method{writelines()} methods. These methods expect Unicode strings, -translating them to the given encoding on output. -\end{itemize} - -For example, the following code writes a Unicode string into a file, -encoding it as UTF-8: - -\begin{verbatim} -import codecs - -unistr = u'\u0660\u2000ab ...' - -(UTF8_encode, UTF8_decode, - UTF8_streamreader, UTF8_streamwriter) = codecs.lookup('UTF-8') - -output = UTF8_streamwriter( open( '/tmp/output', 'wb') ) -output.write( unistr ) -output.close() -\end{verbatim} - -The following code would then read UTF-8 input from the file: - -\begin{verbatim} -input = UTF8_streamreader( open( '/tmp/output', 'rb') ) -print repr(input.read()) -input.close() -\end{verbatim} - -Unicode-aware regular expressions are available through the -\module{re} module, which has a new underlying implementation called -SRE written by Fredrik Lundh of Secret Labs AB. - -A \code{-U} command line option was added which causes the Python -compiler to interpret all string literals as Unicode string literals. -This is intended to be used in testing and future-proofing your Python -code, since some future version of Python may drop support for 8-bit -strings and provide only Unicode strings. - -% ====================================================================== -\section{List Comprehensions} - -Lists are a workhorse data type in Python, and many programs -manipulate a list at some point. Two common operations on lists are -to loop over them, and either pick out the elements that meet a -certain criterion, or apply some function to each element. For -example, given a list of strings, you might want to pull out all the -strings containing a given substring, or strip off trailing whitespace -from each line. - -The existing \function{map()} and \function{filter()} functions can be -used for this purpose, but they require a function as one of their -arguments. This is fine if there's an existing built-in function that -can be passed directly, but if there isn't, you have to create a -little function to do the required work, and Python's scoping rules -make the result ugly if the little function needs additional -information. Take the first example in the previous paragraph, -finding all the strings in the list containing a given substring. You -could write the following to do it: - -\begin{verbatim} -# Given the list L, make a list of all strings -# containing the substring S. -sublist = filter( lambda s, substring=S: - string.find(s, substring) != -1, - L) -\end{verbatim} - -Because of Python's scoping rules, a default argument is used so that -the anonymous function created by the \keyword{lambda} statement knows -what substring is being searched for. List comprehensions make this -cleaner: - -\begin{verbatim} -sublist = [ s for s in L if string.find(s, S) != -1 ] -\end{verbatim} - -List comprehensions have the form: - -\begin{verbatim} -[ expression for expr in sequence1 - for expr2 in sequence2 ... - for exprN in sequenceN - if condition -\end{verbatim} - -The \keyword{for}...\keyword{in} clauses contain the sequences to be -iterated over. The sequences do not have to be the same length, -because they are \emph{not} iterated over in parallel, but -from left to right; this is explained more clearly in the following -paragraphs. The elements of the generated list will be the successive -values of \var{expression}. The final \keyword{if} clause is -optional; if present, \var{expression} is only evaluated and added to -the result if \var{condition} is true. - -To make the semantics very clear, a list comprehension is equivalent -to the following Python code: - -\begin{verbatim} -for expr1 in sequence1: - for expr2 in sequence2: - ... - for exprN in sequenceN: - if (condition): - # Append the value of - # the expression to the - # resulting list. -\end{verbatim} - -This means that when there are \keyword{for}...\keyword{in} clauses, -the resulting list will be equal to the product of the lengths of all -the sequences. If you have two lists of length 3, the output list is -9 elements long: - -\begin{verbatim} -seq1 = 'abc' -seq2 = (1,2,3) ->>> [ (x,y) for x in seq1 for y in seq2] -[('a', 1), ('a', 2), ('a', 3), ('b', 1), ('b', 2), ('b', 3), ('c', 1), -('c', 2), ('c', 3)] -\end{verbatim} - -To avoid introducing an ambiguity into Python's grammar, if -\var{expression} is creating a tuple, it must be surrounded with -parentheses. The first list comprehension below is a syntax error, -while the second one is correct: - -\begin{verbatim} -# Syntax error -[ x,y for x in seq1 for y in seq2] -# Correct -[ (x,y) for x in seq1 for y in seq2] -\end{verbatim} - -The idea of list comprehensions originally comes from the functional -programming language Haskell (\url{http://www.haskell.org}). Greg -Ewing argued most effectively for adding them to Python and wrote the -initial list comprehension patch, which was then discussed for a -seemingly endless time on the python-dev mailing list and kept -up-to-date by Skip Montanaro. - -% ====================================================================== -\section{Augmented Assignment} - -Augmented assignment operators, another long-requested feature, have -been added to Python 2.0. Augmented assignment operators include -\code{+=}, \code{-=}, \code{*=}, and so forth. For example, the -statement \code{a += 2} increments the value of the variable -\code{a} by 2, equivalent to the slightly lengthier \code{a = a + 2}. - -The full list of supported assignment operators is \code{+=}, -\code{-=}, \code{*=}, \code{/=}, \code{\%=}, \code{**=}, \code{\&=}, -\code{|=}, \verb|^=|, \code{>>=}, and \code{<<=}. Python classes can -override the augmented assignment operators by defining methods named -\method{__iadd__}, \method{__isub__}, etc. For example, the following -\class{Number} class stores a number and supports using += to create a -new instance with an incremented value. - -\begin{verbatim} -class Number: - def __init__(self, value): - self.value = value - def __iadd__(self, increment): - return Number( self.value + increment) - -n = Number(5) -n += 3 -print n.value -\end{verbatim} - -The \method{__iadd__} special method is called with the value of the -increment, and should return a new instance with an appropriately -modified value; this return value is bound as the new value of the -variable on the left-hand side. - -Augmented assignment operators were first introduced in the C -programming language, and most C-derived languages, such as -\program{awk}, C++, Java, Perl, and PHP also support them. The augmented -assignment patch was implemented by Thomas Wouters. - -% ====================================================================== -\section{String Methods} - -Until now string-manipulation functionality was in the \module{string} -module, which was usually a front-end for the \module{strop} -module written in C. The addition of Unicode posed a difficulty for -the \module{strop} module, because the functions would all need to be -rewritten in order to accept either 8-bit or Unicode strings. For -functions such as \function{string.replace()}, which takes 3 string -arguments, that means eight possible permutations, and correspondingly -complicated code. - -Instead, Python 2.0 pushes the problem onto the string type, making -string manipulation functionality available through methods on both -8-bit strings and Unicode strings. - -\begin{verbatim} ->>> 'andrew'.capitalize() -'Andrew' ->>> 'hostname'.replace('os', 'linux') -'hlinuxtname' ->>> 'moshe'.find('sh') -2 -\end{verbatim} - -One thing that hasn't changed, a noteworthy April Fools' joke -notwithstanding, is that Python strings are immutable. Thus, the -string methods return new strings, and do not modify the string on -which they operate. - -The old \module{string} module is still around for backwards -compatibility, but it mostly acts as a front-end to the new string -methods. - -Two methods which have no parallel in pre-2.0 versions, although they -did exist in JPython for quite some time, are \method{startswith()} -and \method{endswith}. \code{s.startswith(t)} is equivalent to \code{s[:len(t)] -== t}, while \code{s.endswith(t)} is equivalent to \code{s[-len(t):] == t}. - -One other method which deserves special mention is \method{join}. The -\method{join} method of a string receives one parameter, a sequence of -strings, and is equivalent to the \function{string.join} function from -the old \module{string} module, with the arguments reversed. In other -words, \code{s.join(seq)} is equivalent to the old -\code{string.join(seq, s)}. - -% ====================================================================== -\section{Garbage Collection of Cycles} - -The C implementation of Python uses reference counting to implement -garbage collection. Every Python object maintains a count of the -number of references pointing to itself, and adjusts the count as -references are created or destroyed. Once the reference count reaches -zero, the object is no longer accessible, since you need to have a -reference to an object to access it, and if the count is zero, no -references exist any longer. - -Reference counting has some pleasant properties: it's easy to -understand and implement, and the resulting implementation is -portable, fairly fast, and reacts well with other libraries that -implement their own memory handling schemes. The major problem with -reference counting is that it sometimes doesn't realise that objects -are no longer accessible, resulting in a memory leak. This happens -when there are cycles of references. - -Consider the simplest possible cycle, -a class instance which has a reference to itself: - -\begin{verbatim} -instance = SomeClass() -instance.myself = instance -\end{verbatim} - -After the above two lines of code have been executed, the reference -count of \code{instance} is 2; one reference is from the variable -named \samp{'instance'}, and the other is from the \samp{myself} -attribute of the instance. - -If the next line of code is \code{del instance}, what happens? The -reference count of \code{instance} is decreased by 1, so it has a -reference count of 1; the reference in the \samp{myself} attribute -still exists. Yet the instance is no longer accessible through Python -code, and it could be deleted. Several objects can participate in a -cycle if they have references to each other, causing all of the -objects to be leaked. - -Python 2.0 fixes this problem by periodically executing a cycle -detection algorithm which looks for inaccessible cycles and deletes -the objects involved. A new \module{gc} module provides functions to -perform a garbage collection, obtain debugging statistics, and tuning -the collector's parameters. - -Running the cycle detection algorithm takes some time, and therefore -will result in some additional overhead. It is hoped that after we've -gotten experience with the cycle collection from using 2.0, Python 2.1 -will be able to minimize the overhead with careful tuning. It's not -yet obvious how much performance is lost, because benchmarking this is -tricky and depends crucially on how often the program creates and -destroys objects. The detection of cycles can be disabled when Python -is compiled, if you can't afford even a tiny speed penalty or suspect -that the cycle collection is buggy, by specifying the -\samp{--without-cycle-gc} switch when running the \file{configure} -script. - -Several people tackled this problem and contributed to a solution. An -early implementation of the cycle detection approach was written by -Toby Kelsey. The current algorithm was suggested by Eric Tiedemann -during a visit to CNRI, and Guido van Rossum and Neil Schemenauer -wrote two different implementations, which were later integrated by -Neil. Lots of other people offered suggestions along the way; the -March 2000 archives of the python-dev mailing list contain most of the -relevant discussion, especially in the threads titled ``Reference -cycle collection for Python'' and ``Finalization again''. - -% ====================================================================== -\section{Other Core Changes} - -Various minor changes have been made to Python's syntax and built-in -functions. None of the changes are very far-reaching, but they're -handy conveniences. - -\subsection{Minor Language Changes} - -A new syntax makes it more convenient to call a given function -with a tuple of arguments and/or a dictionary of keyword arguments. -In Python 1.5 and earlier, you'd use the \function{apply()} -built-in function: \code{apply(f, \var{args}, \var{kw})} calls the -function \function{f()} with the argument tuple \var{args} and the -keyword arguments in the dictionary \var{kw}. \function{apply()} -is the same in 2.0, but thanks to a patch from -Greg Ewing, \code{f(*\var{args}, **\var{kw})} as a shorter -and clearer way to achieve the same effect. This syntax is -symmetrical with the syntax for defining functions: - -\begin{verbatim} -def f(*args, **kw): - # args is a tuple of positional args, - # kw is a dictionary of keyword args - ... -\end{verbatim} - -The \keyword{print} statement can now have its output directed to a -file-like object by following the \keyword{print} with -\verb|>> file|, similar to the redirection operator in Unix shells. -Previously you'd either have to use the \method{write()} method of the -file-like object, which lacks the convenience and simplicity of -\keyword{print}, or you could assign a new value to -\code{sys.stdout} and then restore the old value. For sending output to standard error, -it's much easier to write this: - -\begin{verbatim} -print >> sys.stderr, "Warning: action field not supplied" -\end{verbatim} - -Modules can now be renamed on importing them, using the syntax -\code{import \var{module} as \var{name}} or \code{from \var{module} -import \var{name} as \var{othername}}. The patch was submitted by -Thomas Wouters. - -A new format style is available when using the \code{\%} operator; -'\%r' will insert the \function{repr()} of its argument. This was -also added from symmetry considerations, this time for symmetry with -the existing '\%s' format style, which inserts the \function{str()} of -its argument. For example, \code{'\%r \%s' \% ('abc', 'abc')} returns a -string containing \verb|'abc' abc|. - -Previously there was no way to implement a class that overrode -Python's built-in \keyword{in} operator and implemented a custom -version. \code{\var{obj} in \var{seq}} returns true if \var{obj} is -present in the sequence \var{seq}; Python computes this by simply -trying every index of the sequence until either \var{obj} is found or -an \exception{IndexError} is encountered. Moshe Zadka contributed a -patch which adds a \method{__contains__} magic method for providing a -custom implementation for \keyword{in}. Additionally, new built-in -objects written in C can define what \keyword{in} means for them via a -new slot in the sequence protocol. - -Earlier versions of Python used a recursive algorithm for deleting -objects. Deeply nested data structures could cause the interpreter to -fill up the C stack and crash; Christian Tismer rewrote the deletion -logic to fix this problem. On a related note, comparing recursive -objects recursed infinitely and crashed; Jeremy Hylton rewrote the -code to no longer crash, producing a useful result instead. For -example, after this code: - -\begin{verbatim} -a = [] -b = [] -a.append(a) -b.append(b) -\end{verbatim} - -The comparison \code{a==b} returns true, because the two recursive -data structures are isomorphic. See the thread ``trashcan -and PR\#7'' in the April 2000 archives of the python-dev mailing list -for the discussion leading up to this implementation, and some useful -relevant links. -% Starting URL: -% http://www.python.org/pipermail/python-dev/2000-April/004834.html - -Note that comparisons can now also raise exceptions. In earlier -versions of Python, a comparison operation such as \code{cmp(a,b)} -would always produce an answer, even if a user-defined -\method{__cmp__} method encountered an error, since the resulting -exception would simply be silently swallowed. - -Work has been done on porting Python to 64-bit Windows on the Itanium -processor, mostly by Trent Mick of ActiveState. (Confusingly, -\code{sys.platform} is still \code{'win32'} on Win64 because it seems -that for ease of porting, MS Visual C++ treats code as 32 bit on Itanium.) -PythonWin also supports Windows CE; see the Python CE page at -\url{http://starship.python.net/crew/mhammond/ce/} for more -information. - -Another new platform is Darwin/MacOS X; inital support for it is in -Python 2.0. Dynamic loading works, if you specify ``configure ---with-dyld --with-suffix=.x''. Consult the README in the Python -source distribution for more instructions. - -An attempt has been made to alleviate one of Python's warts, the -often-confusing \exception{NameError} exception when code refers to a -local variable before the variable has been assigned a value. For -example, the following code raises an exception on the \keyword{print} -statement in both 1.5.2 and 2.0; in 1.5.2 a \exception{NameError} -exception is raised, while 2.0 raises a new -\exception{UnboundLocalError} exception. -\exception{UnboundLocalError} is a subclass of \exception{NameError}, -so any existing code that expects \exception{NameError} to be raised -should still work. - -\begin{verbatim} -def f(): - print "i=",i - i = i + 1 -f() -\end{verbatim} - -Two new exceptions, \exception{TabError} and -\exception{IndentationError}, have been introduced. They're both -subclasses of \exception{SyntaxError}, and are raised when Python code -is found to be improperly indented. - -\subsection{Changes to Built-in Functions} - -A new built-in, \function{zip(\var{seq1}, \var{seq2}, ...)}, has been -added. \function{zip()} returns a list of tuples where each tuple -contains the i-th element from each of the argument sequences. The -difference between \function{zip()} and \code{map(None, \var{seq1}, -\var{seq2})} is that \function{map()} pads the sequences with -\code{None} if the sequences aren't all of the same length, while -\function{zip()} truncates the returned list to the length of the -shortest argument sequence. - -The \function{int()} and \function{long()} functions now accept an -optional ``base'' parameter when the first argument is a string. -\code{int('123', 10)} returns 123, while \code{int('123', 16)} returns -291. \code{int(123, 16)} raises a \exception{TypeError} exception -with the message ``can't convert non-string with explicit base''. - -A new variable holding more detailed version information has been -added to the \module{sys} module. \code{sys.version_info} is a tuple -\code{(\var{major}, \var{minor}, \var{micro}, \var{level}, -\var{serial})} For example, in a hypothetical 2.0.1beta1, -\code{sys.version_info} would be \code{(2, 0, 1, 'beta', 1)}. -\var{level} is a string such as \code{"alpha"}, \code{"beta"}, or -\code{"final"} for a final release. - -Dictionaries have an odd new method, \method{setdefault(\var{key}, -\var{default})}, which behaves similarly to the existing -\method{get()} method. However, if the key is missing, -\method{setdefault()} both returns the value of \var{default} as -\method{get()} would do, and also inserts it into the dictionary as -the value for \var{key}. Thus, the following lines of code: - -\begin{verbatim} -if dict.has_key( key ): return dict[key] -else: - dict[key] = [] - return dict[key] -\end{verbatim} - -can be reduced to a single \code{return dict.setdefault(key, [])} statement. - -The interpreter sets a maximum recursion depth in order to catch -runaway recursion before filling the C stack and causing a core dump -or GPF.. Previously this limit was fixed when you compiled Python, -but in 2.0 the maximum recursion depth can be read and modified using -\function{sys.getrecursionlimit} and \function{sys.setrecursionlimit}. -The default value is 1000, and a rough maximum value for a given -platform can be found by running a new script, -\file{Misc/find_recursionlimit.py}. - -% ====================================================================== -\section{Porting to 2.0} - -New Python releases try hard to be compatible with previous releases, -and the record has been pretty good. However, some changes are -considered useful enough, usually because they fix initial design decisions that -turned out to be actively mistaken, that breaking backward compatibility -can't always be avoided. This section lists the changes in Python 2.0 -that may cause old Python code to break. - -The change which will probably break the most code is tightening up -the arguments accepted by some methods. Some methods would take -multiple arguments and treat them as a tuple, particularly various -list methods such as \method{.append()} and \method{.insert()}. -In earlier versions of Python, if \code{L} is a list, \code{L.append( -1,2 )} appends the tuple \code{(1,2)} to the list. In Python 2.0 this -causes a \exception{TypeError} exception to be raised, with the -message: 'append requires exactly 1 argument; 2 given'. The fix is to -simply add an extra set of parentheses to pass both values as a tuple: -\code{L.append( (1,2) )}. - -The earlier versions of these methods were more forgiving because they -used an old function in Python's C interface to parse their arguments; -2.0 modernizes them to use \function{PyArg_ParseTuple}, the current -argument parsing function, which provides more helpful error messages -and treats multi-argument calls as errors. If you absolutely must use -2.0 but can't fix your code, you can edit \file{Objects/listobject.c} -and define the preprocessor symbol \code{NO_STRICT_LIST_APPEND} to -preserve the old behaviour; this isn't recommended. - -Some of the functions in the \module{socket} module are still -forgiving in this way. For example, \function{socket.connect( -('hostname', 25) )} is the correct form, passing a tuple representing -an IP address, but \function{socket.connect( 'hostname', 25 )} also -works. \function{socket.connect_ex()} and \function{socket.bind()} are -similarly easy-going. 2.0alpha1 tightened these functions up, but -because the documentation actually used the erroneous multiple -argument form, many people wrote code which would break with the -stricter checking. GvR backed out the changes in the face of public -reaction, so for the \module{socket} module, the documentation was -fixed and the multiple argument form is simply marked as deprecated; -it \emph{will} be tightened up again in a future Python version. - -The \code{\e x} escape in string literals now takes exactly 2 hex -digits. Previously it would consume all the hex digits following the -'x' and take the lowest 8 bits of the result, so \code{\e x123456} was -equivalent to \code{\e x56}. - -The \exception{AttributeError} exception has a more friendly error message, -whose text will be something like \code{'Spam' instance has no attribute 'eggs'}. -Previously the error message was just the missing attribute name \code{eggs}, and -code written to take advantage of this fact will break in 2.0. - -Some work has been done to make integers and long integers a bit more -interchangeable. In 1.5.2, large-file support was added for Solaris, -to allow reading files larger than 2Gb; this made the \method{tell()} -method of file objects return a long integer instead of a regular -integer. Some code would subtract two file offsets and attempt to use -the result to multiply a sequence or slice a string, but this raised a -\exception{TypeError}. In 2.0, long integers can be used to multiply -or slice a sequence, and it'll behave as you'd intuitively expect it -to; \code{3L * 'abc'} produces 'abcabcabc', and \code{ -(0,1,2,3)[2L:4L]} produces (2,3). Long integers can also be used in -various contexts where previously only integers were accepted, such -as in the \method{seek()} method of file objects, and in the formats -supported by the \verb|%| operator (\verb|%d|, \verb|%i|, \verb|%x|, -etc.). For example, \code{"\%d" \% 2L**64} will produce the string -\samp{18446744073709551616}. - -The subtlest long integer change of all is that the \function{str()} -of a long integer no longer has a trailing 'L' character, though -\function{repr()} still includes it. The 'L' annoyed many people who -wanted to print long integers that looked just like regular integers, -since they had to go out of their way to chop off the character. This -is no longer a problem in 2.0, but code which does \code{str(longval)[:-1]} and assumes the 'L' is there, will now lose -the final digit. - -Taking the \function{repr()} of a float now uses a different -formatting precision than \function{str()}. \function{repr()} uses -\code{\%.17g} format string for C's \function{sprintf()}, while -\function{str()} uses \code{\%.12g} as before. The effect is that -\function{repr()} may occasionally show more decimal places than -\function{str()}, for certain numbers. -For example, the number 8.1 can't be represented exactly in binary, so -\code{repr(8.1)} is \code{'8.0999999999999996'}, while str(8.1) is -\code{'8.1'}. - -The \code{-X} command-line option, which turned all standard -exceptions into strings instead of classes, has been removed; the -standard exceptions will now always be classes. The -\module{exceptions} module containing the standard exceptions was -translated from Python to a built-in C module, written by Barry Warsaw -and Fredrik Lundh. - -% Commented out for now -- I don't think anyone will care. -%The pattern and match objects provided by SRE are C types, not Python -%class instances as in 1.5. This means you can no longer inherit from -%\class{RegexObject} or \class{MatchObject}, but that shouldn't be much -%of a problem since no one should have been doing that in the first -%place. - -% ====================================================================== -\section{Extending/Embedding Changes} - -Some of the changes are under the covers, and will only be apparent to -people writing C extension modules or embedding a Python interpreter -in a larger application. If you aren't dealing with Python's C API, -you can safely skip this section. - -The version number of the Python C API was incremented, so C -extensions compiled for 1.5.2 must be recompiled in order to work with -2.0. On Windows, it's not possible for Python 2.0 to import a third -party extension built for Python 1.5.x due to how Windows DLLs work, -so Python will raise an exception and the import will fail. - -Users of Jim Fulton's ExtensionClass module will be pleased to find -out that hooks have been added so that ExtensionClasses are now -supported by \function{isinstance()} and \function{issubclass()}. -This means you no longer have to remember to write code such as -\code{if type(obj) == myExtensionClass}, but can use the more natural -\code{if isinstance(obj, myExtensionClass)}. - -The \file{Python/importdl.c} file, which was a mass of \#ifdefs to -support dynamic loading on many different platforms, was cleaned up -and reorganised by Greg Stein. \file{importdl.c} is now quite small, -and platform-specific code has been moved into a bunch of -\file{Python/dynload_*.c} files. Another cleanup: there were also a -number of \file{my*.h} files in the Include/ directory that held -various portability hacks; they've been merged into a single file, -\file{Include/pyport.h}. - -Vladimir Marangozov's long-awaited malloc restructuring was completed, -to make it easy to have the Python interpreter use a custom allocator -instead of C's standard \function{malloc()}. For documentation, read -the comments in \file{Include/pymem.h} and -\file{Include/objimpl.h}. For the lengthy discussions during which -the interface was hammered out, see the Web archives of the 'patches' -and 'python-dev' lists at python.org. - -Recent versions of the GUSI development environment for MacOS support -POSIX threads. Therefore, Python's POSIX threading support now works -on the Macintosh. Threading support using the user-space GNU \texttt{pth} -library was also contributed. - -Threading support on Windows was enhanced, too. Windows supports -thread locks that use kernel objects only in case of contention; in -the common case when there's no contention, they use simpler functions -which are an order of magnitude faster. A threaded version of Python -1.5.2 on NT is twice as slow as an unthreaded version; with the 2.0 -changes, the difference is only 10\%. These improvements were -contributed by Yakov Markovitch. - -Python 2.0's source now uses only ANSI C prototypes, so compiling Python now -requires an ANSI C compiler, and can no longer be done using a compiler that -only supports K\&R C. - -Previously the Python virtual machine used 16-bit numbers in its -bytecode, limiting the size of source files. In particular, this -affected the maximum size of literal lists and dictionaries in Python -source; occasionally people who are generating Python code would run -into this limit. A patch by Charles G. Waldman raises the limit from -\verb|2^16| to \verb|2^{32}|. - -Three new convenience functions intended for adding constants to a -module's dictionary at module initialization time were added: -\function{PyModule_AddObject()}, \function{PyModule_AddIntConstant()}, -and \function{PyModule_AddStringConstant()}. Each of these functions -takes a module object, a null-terminated C string containing the name -to be added, and a third argument for the value to be assigned to the -name. This third argument is, respectively, a Python object, a C -long, or a C string. - -A wrapper API was added for Unix-style signal handlers. -\function{PyOS_getsig()} gets a signal handler and -\function{PyOS_setsig()} will set a new handler. - -% ====================================================================== -\section{Distutils: Making Modules Easy to Install} - -Before Python 2.0, installing modules was a tedious affair -- there -was no way to figure out automatically where Python is installed, or -what compiler options to use for extension modules. Software authors -had to go through an arduous ritual of editing Makefiles and -configuration files, which only really work on Unix and leave Windows -and MacOS unsupported. Python users faced wildly differing -installation instructions which varied between different extension -packages, which made adminstering a Python installation something of a -chore. - -The SIG for distribution utilities, shepherded by Greg Ward, has -created the Distutils, a system to make package installation much -easier. They form the \module{distutils} package, a new part of -Python's standard library. In the best case, installing a Python -module from source will require the same steps: first you simply mean -unpack the tarball or zip archive, and the run ``\code{python setup.py -install}''. The platform will be automatically detected, the compiler -will be recognized, C extension modules will be compiled, and the -distribution installed into the proper directory. Optional -command-line arguments provide more control over the installation -process, the distutils package offers many places to override defaults --- separating the build from the install, building or installing in -non-default directories, and more. - -In order to use the Distutils, you need to write a \file{setup.py} -script. For the simple case, when the software contains only .py -files, a minimal \file{setup.py} can be just a few lines long: - -\begin{verbatim} -from distutils.core import setup -setup (name = "foo", version = "1.0", - py_modules = ["module1", "module2"]) -\end{verbatim} - -The \file{setup.py} file isn't much more complicated if the software -consists of a few packages: - -\begin{verbatim} -from distutils.core import setup -setup (name = "foo", version = "1.0", - packages = ["package", "package.subpackage"]) -\end{verbatim} - -A C extension can be the most complicated case; here's an example taken from -the PyXML package: - - -\begin{verbatim} -from distutils.core import setup, Extension - -expat_extension = Extension('xml.parsers.pyexpat', - define_macros = [('XML_NS', None)], - include_dirs = [ 'extensions/expat/xmltok', - 'extensions/expat/xmlparse' ], - sources = [ 'extensions/pyexpat.c', - 'extensions/expat/xmltok/xmltok.c', - 'extensions/expat/xmltok/xmlrole.c', - ] - ) -setup (name = "PyXML", version = "0.5.4", - ext_modules =[ expat_extension ] ) -\end{verbatim} - -The Distutils can also take care of creating source and binary -distributions. The ``sdist'' command, run by ``\code{python setup.py -sdist}', builds a source distribution such as \file{foo-1.0.tar.gz}. -Adding new commands isn't difficult, ``bdist_rpm'' and -``bdist_wininst'' commands have already been contributed to create an -RPM distribution and a Windows installer for the software, -respectively. Commands to create other distribution formats such as -Debian packages and Solaris \file{.pkg} files are in various stages of -development. - -All this is documented in a new manual, \textit{Distributing Python -Modules}, that joins the basic set of Python documentation. - -% ====================================================================== -\section{XML Modules} - -Python 1.5.2 included a simple XML parser in the form of the -\module{xmllib} module, contributed by Sjoerd Mullender. Since -1.5.2's release, two different interfaces for processing XML have -become common: SAX2 (version 2 of the Simple API for XML) provides an -event-driven interface with some similarities to \module{xmllib}, and -the DOM (Document Object Model) provides a tree-based interface, -transforming an XML document into a tree of nodes that can be -traversed and modified. Python 2.0 includes a SAX2 interface and a -stripped-down DOM interface as part of the \module{xml} package. -Here we will give a brief overview of these new interfaces; consult -the Python documentation or the source code for complete details. -The Python XML SIG is also working on improved documentation. - -\subsection{SAX2 Support} - -SAX defines an event-driven interface for parsing XML. To use SAX, -you must write a SAX handler class. Handler classes inherit from -various classes provided by SAX, and override various methods that -will then be called by the XML parser. For example, the -\method{startElement} and \method{endElement} methods are called for -every starting and end tag encountered by the parser, the -\method{characters()} method is called for every chunk of character -data, and so forth. - -The advantage of the event-driven approach is that that the whole -document doesn't have to be resident in memory at any one time, which -matters if you are processing really huge documents. However, writing -the SAX handler class can get very complicated if you're trying to -modify the document structure in some elaborate way. - -For example, this little example program defines a handler that prints -a message for every starting and ending tag, and then parses the file -\file{hamlet.xml} using it: - -\begin{verbatim} -from xml import sax - -class SimpleHandler(sax.ContentHandler): - def startElement(self, name, attrs): - print 'Start of element:', name, attrs.keys() - - def endElement(self, name): - print 'End of element:', name - -# Create a parser object -parser = sax.make_parser() - -# Tell it what handler to use -handler = SimpleHandler() -parser.setContentHandler( handler ) - -# Parse a file! -parser.parse( 'hamlet.xml' ) -\end{verbatim} - -For more information, consult the Python documentation, or the XML -HOWTO at \url{http://www.python.org/doc/howto/xml/}. - -\subsection{DOM Support} - -The Document Object Model is a tree-based representation for an XML -document. A top-level \class{Document} instance is the root of the -tree, and has a single child which is the top-level \class{Element} -instance. This \class{Element} has children nodes representing -character data and any sub-elements, which may have further children -of their own, and so forth. Using the DOM you can traverse the -resulting tree any way you like, access element and attribute values, -insert and delete nodes, and convert the tree back into XML. - -The DOM is useful for modifying XML documents, because you can create -a DOM tree, modify it by adding new nodes or rearranging subtrees, and -then produce a new XML document as output. You can also construct a -DOM tree manually and convert it to XML, which can be a more flexible -way of producing XML output than simply writing -\code{}...\code{} to a file. - -The DOM implementation included with Python lives in the -\module{xml.dom.minidom} module. It's a lightweight implementation of -the Level 1 DOM with support for XML namespaces. The -\function{parse()} and \function{parseString()} convenience -functions are provided for generating a DOM tree: - -\begin{verbatim} -from xml.dom import minidom -doc = minidom.parse('hamlet.xml') -\end{verbatim} - -\code{doc} is a \class{Document} instance. \class{Document}, like all -the other DOM classes such as \class{Element} and \class{Text}, is a -subclass of the \class{Node} base class. All the nodes in a DOM tree -therefore support certain common methods, such as \method{toxml()} -which returns a string containing the XML representation of the node -and its children. Each class also has special methods of its own; for -example, \class{Element} and \class{Document} instances have a method -to find all child elements with a given tag name. Continuing from the -previous 2-line example: - -\begin{verbatim} -perslist = doc.getElementsByTagName( 'PERSONA' ) -print perslist[0].toxml() -print perslist[1].toxml() -\end{verbatim} - -For the \textit{Hamlet} XML file, the above few lines output: - -\begin{verbatim} -CLAUDIUS, king of Denmark. -HAMLET, son to the late, and nephew to the present king. -\end{verbatim} - -The root element of the document is available as -\code{doc.documentElement}, and its children can be easily modified -by deleting, adding, or removing nodes: - -\begin{verbatim} -root = doc.documentElement - -# Remove the first child -root.removeChild( root.childNodes[0] ) - -# Move the new first child to the end -root.appendChild( root.childNodes[0] ) - -# Insert the new first child (originally, -# the third child) before the 20th child. -root.insertBefore( root.childNodes[0], root.childNodes[20] ) -\end{verbatim} - -Again, I will refer you to the Python documentation for a complete -listing of the different \class{Node} classes and their various methods. - -\subsection{Relationship to PyXML} - -The XML Special Interest Group has been working on XML-related Python -code for a while. Its code distribution, called PyXML, is available -from the SIG's Web pages at \url{http://www.python.org/sigs/xml-sig/}. -The PyXML distribution also used the package name \samp{xml}. If -you've written programs that used PyXML, you're probably wondering -about its compatibility with the 2.0 \module{xml} package. - -The answer is that Python 2.0's \module{xml} package isn't compatible -with PyXML, but can be made compatible by installing a recent version -PyXML. Many applications can get by with the XML support that is -included with Python 2.0, but more complicated applications will -require that the full PyXML package will be installed. When -installed, PyXML versions 0.6.0 or greater will replace the -\module{xml} package shipped with Python, and will be a strict -superset of the standard package, adding a bunch of additional -features. Some of the additional features in PyXML include: - -\begin{itemize} -\item 4DOM, a full DOM implementation -from FourThought, Inc. -\item The xmlproc validating parser, written by Lars Marius Garshol. -\item The \module{sgmlop} parser accelerator module, written by Fredrik Lundh. -\end{itemize} - -% ====================================================================== -\section{Module changes} - -Lots of improvements and bugfixes were made to Python's extensive -standard library; some of the affected modules include -\module{readline}, \module{ConfigParser}, \module{cgi}, -\module{calendar}, \module{posix}, \module{readline}, \module{xmllib}, -\module{aifc}, \module{chunk, wave}, \module{random}, \module{shelve}, -and \module{nntplib}. Consult the CVS logs for the exact -patch-by-patch details. - -Brian Gallew contributed OpenSSL support for the \module{socket} -module. OpenSSL is an implementation of the Secure Socket Layer, -which encrypts the data being sent over a socket. When compiling -Python, you can edit \file{Modules/Setup} to include SSL support, -which adds an additional function to the \module{socket} module: -\function{socket.ssl(\var{socket}, \var{keyfile}, \var{certfile})}, -which takes a socket object and returns an SSL socket. The -\module{httplib} and \module{urllib} modules were also changed to -support ``https://'' URLs, though no one has implemented FTP or SMTP -over SSL. - -The \module{httplib} module has been rewritten by Greg Stein to -support HTTP/1.1. Backward compatibility with the 1.5 version of -\module{httplib} is provided, though using HTTP/1.1 features such as -pipelining will require rewriting code to use a different set of -interfaces. - -The \module{Tkinter} module now supports Tcl/Tk version 8.1, 8.2, or -8.3, and support for the older 7.x versions has been dropped. The -Tkinter module now supports displaying Unicode strings in Tk widgets. -Also, Fredrik Lundh contributed an optimization which makes operations -like \code{create_line} and \code{create_polygon} much faster, -especially when using lots of coordinates. - -The \module{curses} module has been greatly extended, starting from -Oliver Andrich's enhanced version, to provide many additional -functions from ncurses and SYSV curses, such as colour, alternative -character set support, pads, and mouse support. This means the module -is no longer compatible with operating systems that only have BSD -curses, but there don't seem to be any currently maintained OSes that -fall into this category. - -As mentioned in the earlier discussion of 2.0's Unicode support, the -underlying implementation of the regular expressions provided by the -\module{re} module has been changed. SRE, a new regular expression -engine written by Fredrik Lundh and partially funded by Hewlett -Packard, supports matching against both 8-bit strings and Unicode -strings. - -% ====================================================================== -\section{New modules} - -A number of new modules were added. We'll simply list them with brief -descriptions; consult the 2.0 documentation for the details of a -particular module. - -\begin{itemize} - -\item{\module{atexit}}: -For registering functions to be called before the Python interpreter exits. -Code that currently sets -\code{sys.exitfunc} directly should be changed to -use the \module{atexit} module instead, importing \module{atexit} -and calling \function{atexit.register()} with -the function to be called on exit. -(Contributed by Skip Montanaro.) - -\item{\module{codecs}, \module{encodings}, \module{unicodedata}:} Added as part of the new Unicode support. - -\item{\module{filecmp}:} Supersedes the old \module{cmp}, \module{cmpcache} and -\module{dircmp} modules, which have now become deprecated. -(Contributed by Gordon MacMillan and Moshe Zadka.) - -\item{\module{gettext}:} This module provides internationalization -(I18N) and localization (L10N) support for Python programs by -providing an interface to the GNU gettext message catalog library. -(Integrated by Barry Warsaw, from separate contributions by Martin von -Loewis, Peter Funk, and James Henstridge.) - -\item{\module{linuxaudiodev}:} Support for the \file{/dev/audio} -device on Linux, a twin to the existing \module{sunaudiodev} module. -(Contributed by Peter Bosch, with fixes by Jeremy Hylton.) - -\item{\module{mmap}:} An interface to memory-mapped files on both -Windows and Unix. A file's contents can be mapped directly into -memory, at which point it behaves like a mutable string, so its -contents can be read and modified. They can even be passed to -functions that expect ordinary strings, such as the \module{re} -module. (Contributed by Sam Rushing, with some extensions by -A.M. Kuchling.) - -\item{\module{pyexpat}:} An interface to the Expat XML parser. -(Contributed by Paul Prescod.) - -\item{\module{robotparser}:} Parse a \file{robots.txt} file, which is -used for writing Web spiders that politely avoid certain areas of a -Web site. The parser accepts the contents of a \file{robots.txt} file, -builds a set of rules from it, and can then answer questions about -the fetchability of a given URL. (Contributed by Skip Montanaro.) - -\item{\module{tabnanny}:} A module/script to -check Python source code for ambiguous indentation. -(Contributed by Tim Peters.) - -\item{\module{UserString}:} A base class useful for deriving objects that behave like strings. - -\item{\module{webbrowser}:} A module that provides a platform independent -way to launch a web browser on a specific URL. For each platform, various -browsers are tried in a specific order. The user can alter which browser -is launched by setting the \var{BROWSER} environment variable. -(Originally inspired by Eric S. Raymond's patch to \module{urllib} -which added similar functionality, but -the final module comes from code originally -implemented by Fred Drake as \file{Tools/idle/BrowserControl.py}, -and adapted for the standard library by Fred.) - -\item{\module{_winreg}:} An interface to the -Windows registry. \module{_winreg} is an adaptation of functions that -have been part of PythonWin since 1995, but has now been added to the core -distribution, and enhanced to support Unicode. -\module{_winreg} was written by Bill Tutt and Mark Hammond. - -\item{\module{zipfile}:} A module for reading and writing ZIP-format -archives. These are archives produced by \program{PKZIP} on -DOS/Windows or \program{zip} on Unix, not to be confused with -\program{gzip}-format files (which are supported by the \module{gzip} -module) -(Contributed by James C. Ahlstrom.) - -\item{\module{imputil}:} A module that provides a simpler way for -writing customised import hooks, in comparison to the existing -\module{ihooks} module. (Implemented by Greg Stein, with much -discussion on python-dev along the way.) - -\end{itemize} - -% ====================================================================== -\section{IDLE Improvements} - -IDLE is the official Python cross-platform IDE, written using Tkinter. -Python 2.0 includes IDLE 0.6, which adds a number of new features and -improvements. A partial list: - -\begin{itemize} -\item UI improvements and optimizations, -especially in the area of syntax highlighting and auto-indentation. - -\item The class browser now shows more information, such as the top -level functions in a module. - -\item Tab width is now a user settable option. When opening an existing Python -file, IDLE automatically detects the indentation conventions, and adapts. - -\item There is now support for calling browsers on various platforms, -used to open the Python documentation in a browser. - -\item IDLE now has a command line, which is largely similar to -the vanilla Python interpreter. - -\item Call tips were added in many places. - -\item IDLE can now be installed as a package. - -\item In the editor window, there is now a line/column bar at the bottom. - -\item Three new keystroke commands: Check module (Alt-F5), Import -module (F5) and Run script (Ctrl-F5). - -\end{itemize} - -% ====================================================================== -\section{Deleted and Deprecated Modules} - -A few modules have been dropped because they're obsolete, or because -there are now better ways to do the same thing. The \module{stdwin} -module is gone; it was for a platform-independent windowing toolkit -that's no longer developed. - -A number of modules have been moved to the -\file{lib-old} subdirectory: -\module{cmp}, \module{cmpcache}, \module{dircmp}, \module{dump}, -\module{find}, \module{grep}, \module{packmail}, -\module{poly}, \module{util}, \module{whatsound}, \module{zmod}. -If you have code which relies on a module that's been moved to -\file{lib-old}, you can simply add that directory to \code{sys.path} -to get them back, but you're encouraged to update any code that uses -these modules. - -\section{Acknowledgements} - -The authors would like to thank the following people for offering -suggestions on various drafts of this article: David Bolen, Mark Hammond, Gregg Hauser, -Jeremy Hylton, Fredrik Lundh, Detlef Lannert, Aahz Maruch, Skip -Montanaro, Vladimir Marangozov, Guido van Rossum, Neil Schemenauer, -and Russ Schmidt. - -\end{document} diff --git a/Doc/whatsnew/whatsnew21.tex b/Doc/whatsnew/whatsnew21.tex deleted file mode 100644 index d296f4ce8dee..000000000000 --- a/Doc/whatsnew/whatsnew21.tex +++ /dev/null @@ -1,868 +0,0 @@ -\documentclass{howto} - -\usepackage{distutils} - -% $Id$ - -\title{What's New in Python 2.1} -\release{1.00} -\author{A.M. Kuchling} -\authoraddress{\email{akuchlin@mems-exchange.org}} -\begin{document} -\maketitle\tableofcontents - -\section{Introduction} - -It's that time again... time for a new Python release, Python 2.1. -One recent goal of the Python development team has been to accelerate -the pace of new releases, with a new release coming every 6 to 9 -months. 2.1 is the first release to come out at this faster pace, with -the first alpha appearing in January, 3 months after the final version -of 2.0 was released. - -This article explains the new features in 2.1. While there aren't as -many changes in 2.1 as there were in Python 2.0, there are still some -pleasant surprises in store. 2.1 is the first release to be steered -through the use of Python Enhancement Proposals, or PEPs, so most of -the sizable changes have accompanying PEPs that provide more complete -documentation and a design rationale for the change. This article -doesn't attempt to document the new features completely, but simply -provides an overview of the new features for Python programmers. -Refer to the Python 2.1 documentation, or to the specific PEP, for -more details about any new feature that particularly interests you. - -The final release of Python 2.1 was made on April 17, 2001. - -%====================================================================== -\section{PEP 227: Nested Scopes} - -The largest change in Python 2.1 is to Python's scoping rules. In -Python 2.0, at any given time there are at most three namespaces used -to look up variable names: local, module-level, and the built-in -namespace. This often surprised people because it didn't match their -intuitive expectations. For example, a nested recursive function -definition doesn't work: - -\begin{verbatim} -def f(): - ... - def g(value): - ... - return g(value-1) + 1 - ... -\end{verbatim} - -The function \function{g()} will always raise a \exception{NameError} -exception, because the binding of the name \samp{g} isn't in either -its local namespace or in the module-level namespace. This isn't much -of a problem in practice (how often do you recursively define interior -functions like this?), but this also made using the \keyword{lambda} -statement clumsier, and this was a problem in practice. In code which -uses \keyword{lambda} you can often find local variables being copied -by passing them as the default values of arguments. - -\begin{verbatim} -def find(self, name): - "Return list of any entries equal to 'name'" - L = filter(lambda x, name=name: x == name, - self.list_attribute) - return L -\end{verbatim} - -The readability of Python code written in a strongly functional style -suffers greatly as a result. - -The most significant change to Python 2.1 is that static scoping has -been added to the language to fix this problem. As a first effect, -the \code{name=name} default argument is now unnecessary in the above -example. Put simply, when a given variable name is not assigned a -value within a function (by an assignment, or the \keyword{def}, -\keyword{class}, or \keyword{import} statements), references to the -variable will be looked up in the local namespace of the enclosing -scope. A more detailed explanation of the rules, and a dissection of -the implementation, can be found in the PEP. - -This change may cause some compatibility problems for code where the -same variable name is used both at the module level and as a local -variable within a function that contains further function definitions. -This seems rather unlikely though, since such code would have been -pretty confusing to read in the first place. - -One side effect of the change is that the \code{from \var{module} -import *} and \keyword{exec} statements have been made illegal inside -a function scope under certain conditions. The Python reference -manual has said all along that \code{from \var{module} import *} is -only legal at the top level of a module, but the CPython interpreter -has never enforced this before. As part of the implementation of -nested scopes, the compiler which turns Python source into bytecodes -has to generate different code to access variables in a containing -scope. \code{from \var{module} import *} and \keyword{exec} make it -impossible for the compiler to figure this out, because they add names -to the local namespace that are unknowable at compile time. -Therefore, if a function contains function definitions or -\keyword{lambda} expressions with free variables, the compiler will -flag this by raising a \exception{SyntaxError} exception. - -To make the preceding explanation a bit clearer, here's an example: - -\begin{verbatim} -x = 1 -def f(): - # The next line is a syntax error - exec 'x=2' - def g(): - return x -\end{verbatim} - -Line 4 containing the \keyword{exec} statement is a syntax error, -since \keyword{exec} would define a new local variable named \samp{x} -whose value should be accessed by \function{g()}. - -This shouldn't be much of a limitation, since \keyword{exec} is rarely -used in most Python code (and when it is used, it's often a sign of a -poor design anyway). - -Compatibility concerns have led to nested scopes being introduced -gradually; in Python 2.1, they aren't enabled by default, but can be -turned on within a module by using a future statement as described in -PEP 236. (See the following section for further discussion of PEP -236.) In Python 2.2, nested scopes will become the default and there -will be no way to turn them off, but users will have had all of 2.1's -lifetime to fix any breakage resulting from their introduction. - -\begin{seealso} - -\seepep{227}{Statically Nested Scopes}{Written and implemented by -Jeremy Hylton.} - -\end{seealso} - - -%====================================================================== -\section{PEP 236: \module{__future__} Directives} - -The reaction to nested scopes was widespread concern about the dangers -of breaking code with the 2.1 release, and it was strong enough to -make the Pythoneers take a more conservative approach. This approach -consists of introducing a convention for enabling optional -functionality in release N that will become compulsory in release N+1. - -The syntax uses a \code{from...import} statement using the reserved -module name \module{__future__}. Nested scopes can be enabled by the -following statement: - -\begin{verbatim} -from __future__ import nested_scopes -\end{verbatim} - -While it looks like a normal \keyword{import} statement, it's not; -there are strict rules on where such a future statement can be put. -They can only be at the top of a module, and must precede any Python -code or regular \keyword{import} statements. This is because such -statements can affect how the Python bytecode compiler parses code and -generates bytecode, so they must precede any statement that will -result in bytecodes being produced. - -\begin{seealso} - -\seepep{236}{Back to the \module{__future__}}{Written by Tim Peters, -and primarily implemented by Jeremy Hylton.} - -\end{seealso} - -%====================================================================== -\section{PEP 207: Rich Comparisons} - -In earlier versions, Python's support for implementing comparisons on -user-defined classes and extension types was quite simple. Classes -could implement a \method{__cmp__} method that was given two instances -of a class, and could only return 0 if they were equal or +1 or -1 if -they weren't; the method couldn't raise an exception or return -anything other than a Boolean value. Users of Numeric Python often -found this model too weak and restrictive, because in the -number-crunching programs that numeric Python is used for, it would be -more useful to be able to perform elementwise comparisons of two -matrices, returning a matrix containing the results of a given -comparison for each element. If the two matrices are of different -sizes, then the compare has to be able to raise an exception to signal -the error. - -In Python 2.1, rich comparisons were added in order to support this -need. Python classes can now individually overload each of the -\code{<}, \code{<=}, \code{>}, \code{>=}, \code{==}, and \code{!=} -operations. The new magic method names are: - -\begin{tableii}{c|l}{code}{Operation}{Method name} - \lineii{<}{\method{__lt__}} \lineii{<=}{\method{__le__}} - \lineii{>}{\method{__gt__}} \lineii{>=}{\method{__ge__}} - \lineii{==}{\method{__eq__}} \lineii{!=}{\method{__ne__}} - \end{tableii} - -(The magic methods are named after the corresponding Fortran operators -\code{.LT.}. \code{.LE.}, \&c. Numeric programmers are almost -certainly quite familar with these names and will find them easy to -remember.) - -Each of these magic methods is of the form \code{\var{method}(self, -other)}, where \code{self} will be the object on the left-hand side of -the operator, while \code{other} will be the object on the right-hand -side. For example, the expression \code{A < B} will cause -\code{A.__lt__(B)} to be called. - -Each of these magic methods can return anything at all: a Boolean, a -matrix, a list, or any other Python object. Alternatively they can -raise an exception if the comparison is impossible, inconsistent, or -otherwise meaningless. - -The built-in \function{cmp(A,B)} function can use the rich comparison -machinery, and now accepts an optional argument specifying which -comparison operation to use; this is given as one of the strings -\code{"<"}, \code{"<="}, \code{">"}, \code{">="}, \code{"=="}, or -\code{"!="}. If called without the optional third argument, -\function{cmp()} will only return -1, 0, or +1 as in previous versions -of Python; otherwise it will call the appropriate method and can -return any Python object. - -There are also corresponding changes of interest to C programmers; -there's a new slot \code{tp_richcmp} in type objects and an API for -performing a given rich comparison. I won't cover the C API here, but -will refer you to PEP 207, or to 2.1's C API documentation, for the -full list of related functions. - -\begin{seealso} - -\seepep{207}{Rich Comparisions}{Written by Guido van Rossum, heavily -based on earlier work by David Ascher, and implemented by Guido van -Rossum.} - -\end{seealso} - -%====================================================================== -\section{PEP 230: Warning Framework} - -Over its 10 years of existence, Python has accumulated a certain -number of obsolete modules and features along the way. It's difficult -to know when a feature is safe to remove, since there's no way of -knowing how much code uses it --- perhaps no programs depend on the -feature, or perhaps many do. To enable removing old features in a -more structured way, a warning framework was added. When the Python -developers want to get rid of a feature, it will first trigger a -warning in the next version of Python. The following Python version -can then drop the feature, and users will have had a full release -cycle to remove uses of the old feature. - -Python 2.1 adds the warning framework to be used in this scheme. It -adds a \module{warnings} module that provide functions to issue -warnings, and to filter out warnings that you don't want to be -displayed. Third-party modules can also use this framework to -deprecate old features that they no longer wish to support. - -For example, in Python 2.1 the \module{regex} module is deprecated, so -importing it causes a warning to be printed: - -\begin{verbatim} ->>> import regex -__main__:1: DeprecationWarning: the regex module - is deprecated; please use the re module ->>> -\end{verbatim} - -Warnings can be issued by calling the \function{warnings.warn} -function: - -\begin{verbatim} -warnings.warn("feature X no longer supported") -\end{verbatim} - -The first parameter is the warning message; an additional optional -parameters can be used to specify a particular warning category. - -Filters can be added to disable certain warnings; a regular expression -pattern can be applied to the message or to the module name in order -to suppress a warning. For example, you may have a program that uses -the \module{regex} module and not want to spare the time to convert it -to use the \module{re} module right now. The warning can be -suppressed by calling - -\begin{verbatim} -import warnings -warnings.filterwarnings(action = 'ignore', - message='.*regex module is deprecated', - category=DeprecationWarning, - module = '__main__') -\end{verbatim} - -This adds a filter that will apply only to warnings of the class -\class{DeprecationWarning} triggered in the \module{__main__} module, -and applies a regular expression to only match the message about the -\module{regex} module being deprecated, and will cause such warnings -to be ignored. Warnings can also be printed only once, printed every -time the offending code is executed, or turned into exceptions that -will cause the program to stop (unless the exceptions are caught in -the usual way, of course). - -Functions were also added to Python's C API for issuing warnings; -refer to PEP 230 or to Python's API documentation for the details. - -\begin{seealso} - -\seepep{5}{Guidelines for Language Evolution}{Written -by Paul Prescod, to specify procedures to be followed when removing -old features from Python. The policy described in this PEP hasn't -been officially adopted, but the eventual policy probably won't be too -different from Prescod's proposal.} - -\seepep{230}{Warning Framework}{Written and implemented by Guido van -Rossum.} - -\end{seealso} - -%====================================================================== -\section{PEP 229: New Build System} - -When compiling Python, the user had to go in and edit the -\file{Modules/Setup} file in order to enable various additional -modules; the default set is relatively small and limited to modules -that compile on most Unix platforms. This means that on Unix -platforms with many more features, most notably Linux, Python -installations often don't contain all useful modules they could. - -Python 2.0 added the Distutils, a set of modules for distributing and -installing extensions. In Python 2.1, the Distutils are used to -compile much of the standard library of extension modules, -autodetecting which ones are supported on the current machine. It's -hoped that this will make Python installations easier and more -featureful. - -Instead of having to edit the \file{Modules/Setup} file in order to -enable modules, a \file{setup.py} script in the top directory of the -Python source distribution is run at build time, and attempts to -discover which modules can be enabled by examining the modules and -header files on the system. If a module is configured in -\file{Modules/Setup}, the \file{setup.py} script won't attempt to -compile that module and will defer to the \file{Modules/Setup} file's -contents. This provides a way to specific any strange command-line -flags or libraries that are required for a specific platform. - -In another far-reaching change to the build mechanism, Neil -Schemenauer restructured things so Python now uses a single makefile -that isn't recursive, instead of makefiles in the top directory and in -each of the \file{Python/}, \file{Parser/}, \file{Objects/}, and -\file{Modules/} subdirectories. This makes building Python faster -and also makes hacking the Makefiles clearer and simpler. - -\begin{seealso} - -\seepep{229}{Using Distutils to Build Python}{Written -and implemented by A.M. Kuchling.} - -\end{seealso} - -%====================================================================== -\section{PEP 205: Weak References} - -Weak references, available through the \module{weakref} module, are a -minor but useful new data type in the Python programmer's toolbox. - -Storing a reference to an object (say, in a dictionary or a list) has -the side effect of keeping that object alive forever. There are a few -specific cases where this behaviour is undesirable, object caches -being the most common one, and another being circular references in -data structures such as trees. - -For example, consider a memoizing function that caches the results of -another function \function{f(\var{x})} by storing the function's -argument and its result in a dictionary: - -\begin{verbatim} -_cache = {} -def memoize(x): - if _cache.has_key(x): - return _cache[x] - - retval = f(x) - - # Cache the returned object - _cache[x] = retval - - return retval -\end{verbatim} - -This version works for simple things such as integers, but it has a -side effect; the \code{_cache} dictionary holds a reference to the -return values, so they'll never be deallocated until the Python -process exits and cleans up This isn't very noticeable for integers, -but if \function{f()} returns an object, or a data structure that -takes up a lot of memory, this can be a problem. - -Weak references provide a way to implement a cache that won't keep -objects alive beyond their time. If an object is only accessible -through weak references, the object will be deallocated and the weak -references will now indicate that the object it referred to no longer -exists. A weak reference to an object \var{obj} is created by calling -\code{wr = weakref.ref(\var{obj})}. The object being referred to is -returned by calling the weak reference as if it were a function: -\code{wr()}. It will return the referenced object, or \code{None} if -the object no longer exists. - -This makes it possible to write a \function{memoize()} function whose -cache doesn't keep objects alive, by storing weak references in the -cache. - -\begin{verbatim} -_cache = {} -def memoize(x): - if _cache.has_key(x): - obj = _cache[x]() - # If weak reference object still exists, - # return it - if obj is not None: return obj - - retval = f(x) - - # Cache a weak reference - _cache[x] = weakref.ref(retval) - - return retval -\end{verbatim} - -The \module{weakref} module also allows creating proxy objects which -behave like weak references --- an object referenced only by proxy -objects is deallocated -- but instead of requiring an explicit call to -retrieve the object, the proxy transparently forwards all operations -to the object as long as the object still exists. If the object is -deallocated, attempting to use a proxy will cause a -\exception{weakref.ReferenceError} exception to be raised. - -\begin{verbatim} -proxy = weakref.proxy(obj) -proxy.attr # Equivalent to obj.attr -proxy.meth() # Equivalent to obj.meth() -del obj -proxy.attr # raises weakref.ReferenceError -\end{verbatim} - -\begin{seealso} - -\seepep{205}{Weak References}{Written and implemented by -Fred~L. Drake,~Jr.} - -\end{seealso} - -%====================================================================== -\section{PEP 232: Function Attributes} - -In Python 2.1, functions can now have arbitrary information attached -to them. People were often using docstrings to hold information about -functions and methods, because the \code{__doc__} attribute was the -only way of attaching any information to a function. For example, in -the Zope Web application server, functions are marked as safe for -public access by having a docstring, and in John Aycock's SPARK -parsing framework, docstrings hold parts of the BNF grammar to be -parsed. This overloading is unfortunate, since docstrings are really -intended to hold a function's documentation; for example, it means you -can't properly document functions intended for private use in Zope. - -Arbitrary attributes can now be set and retrieved on functions using the -regular Python syntax: - -\begin{verbatim} -def f(): pass - -f.publish = 1 -f.secure = 1 -f.grammar = "A ::= B (C D)*" -\end{verbatim} - -The dictionary containing attributes can be accessed as the function's -\member{__dict__}. Unlike the \member{__dict__} attribute of class -instances, in functions you can actually assign a new dictionary to -\member{__dict__}, though the new value is restricted to a regular -Python dictionary; you \emph{can't} be tricky and set it to a -\class{UserDict} instance, or any other random object that behaves -like a mapping. - -\begin{seealso} - -\seepep{232}{Function Attributes}{Written and implemented by Barry -Warsaw.} - -\end{seealso} - - -%====================================================================== - -\section{PEP 235: Case-Insensitive Platforms and \keyword{import}} - -Some operating systems have filesystems that are case-insensitive, -MacOS and Windows being the primary examples; on these systems, it's -impossible to distinguish the filenames \samp{FILE.PY} and -\samp{file.py}, even though they do store the file's name -in its original case (they're case-preserving, too). - -In Python 2.1, the \keyword{import} statement will work to simulate -case-sensitivity on case-insensitive platforms. Python will now -search for the first case-sensitive match by default, raising an -\exception{ImportError} if no such file is found, so \code{import file} -will not import a module named \samp{FILE.PY}. Case-insensitive -matching can be requested by setting the \envvar{PYTHONCASEOK} environment -variable before starting the Python interpreter. - -%====================================================================== -\section{PEP 217: Interactive Display Hook} - -When using the Python interpreter interactively, the output of -commands is displayed using the built-in \function{repr()} function. -In Python 2.1, the variable \function{sys.displayhook} can be set to a -callable object which will be called instead of \function{repr()}. -For example, you can set it to a special pretty-printing function: - -\begin{verbatim} ->>> # Create a recursive data structure -... L = [1,2,3] ->>> L.append(L) ->>> L # Show Python's default output -[1, 2, 3, [...]] ->>> # Use pprint.pprint() as the display function -... import sys, pprint ->>> sys.displayhook = pprint.pprint ->>> L -[1, 2, 3, ] ->>> -\end{verbatim} - -\begin{seealso} - -\seepep{217}{Display Hook for Interactive Use}{Written and implemented -by Moshe Zadka.} - -\end{seealso} - -%====================================================================== -\section{PEP 208: New Coercion Model} - -How numeric coercion is done at the C level was significantly -modified. This will only affect the authors of C extensions to -Python, allowing them more flexibility in writing extension types that -support numeric operations. - -Extension types can now set the type flag \code{Py_TPFLAGS_CHECKTYPES} -in their \code{PyTypeObject} structure to indicate that they support -the new coercion model. In such extension types, the numeric slot -functions can no longer assume that they'll be passed two arguments of -the same type; instead they may be passed two arguments of differing -types, and can then perform their own internal coercion. If the slot -function is passed a type it can't handle, it can indicate the failure -by returning a reference to the \code{Py_NotImplemented} singleton -value. The numeric functions of the other type will then be tried, -and perhaps they can handle the operation; if the other type also -returns \code{Py_NotImplemented}, then a \exception{TypeError} will be -raised. Numeric methods written in Python can also return -\code{Py_NotImplemented}, causing the interpreter to act as if the -method did not exist (perhaps raising a \exception{TypeError}, perhaps -trying another object's numeric methods). - -\begin{seealso} - -\seepep{208}{Reworking the Coercion Model}{Written and implemented by -Neil Schemenauer, heavily based upon earlier work by Marc-Andr\'e -Lemburg. Read this to understand the fine points of how numeric -operations will now be processed at the C level.} - -\end{seealso} - -%====================================================================== -\section{PEP 241: Metadata in Python Packages} - -A common complaint from Python users is that there's no single catalog -of all the Python modules in existence. T.~Middleton's Vaults of -Parnassus at \url{http://www.vex.net/parnassus} are the largest -catalog of Python modules, but registering software at the Vaults is -optional, and many people don't bother. - -As a first small step toward fixing the problem, Python software -packaged using the Distutils \command{sdist} command will include a -file named \file{PKG-INFO} containing information about the package -such as its name, version, and author (metadata, in cataloguing -terminology). PEP 241 contains the full list of fields that can be -present in the \file{PKG-INFO} file. As people began to package their -software using Python 2.1, more and more packages will include -metadata, making it possible to build automated cataloguing systems -and experiment with them. With the result experience, perhaps it'll -be possible to design a really good catalog and then build support for -it into Python 2.2. For example, the Distutils \command{sdist} -and \command{bdist_*} commands could support a \option{upload} option -that would automatically upload your package to a catalog server. - -You can start creating packages containing \file{PKG-INFO} even if -you're not using Python 2.1, since a new release of the Distutils will -be made for users of earlier Python versions. Version 1.0.2 of the -Distutils includes the changes described in PEP 241, as well as -various bugfixes and enhancements. It will be available from -the Distutils SIG at \url{http://www.python.org/sigs/distutils-sig}. - -% XXX update when I actually release 1.0.2 - -\begin{seealso} - -\seepep{241}{Metadata for Python Software Packages}{Written and -implemented by A.M. Kuchling.} - -\seepep{243}{Module Repository Upload Mechanism}{Written by Sean -Reifschneider, this draft PEP describes a proposed mechanism for uploading -Python packages to a central server. -} - -\end{seealso} - -%====================================================================== -\section{New and Improved Modules} - -\begin{itemize} - -\item Ka-Ping Yee contributed two new modules: \module{inspect.py}, a -module for getting information about live Python code, and -\module{pydoc.py}, a module for interactively converting docstrings to -HTML or text. As a bonus, \file{Tools/scripts/pydoc}, which is now -automatically installed, uses \module{pydoc.py} to display -documentation given a Python module, package, or class name. For -example, \samp{pydoc xml.dom} displays the following: - -\begin{verbatim} -Python Library Documentation: package xml.dom in xml - -NAME - xml.dom - W3C Document Object Model implementation for Python. - -FILE - /usr/local/lib/python2.1/xml/dom/__init__.pyc - -DESCRIPTION - The Python mapping of the Document Object Model is documented in the - Python Library Reference in the section on the xml.dom package. - - This package contains the following modules: - ... -\end{verbatim} - -\file{pydoc} also includes a Tk-based interactive help browser. -\file{pydoc} quickly becomes addictive; try it out! - -\item Two different modules for unit testing were added to the -standard library. The \module{doctest} module, contributed by Tim -Peters, provides a testing framework based on running embedded -examples in docstrings and comparing the results against the expected -output. PyUnit, contributed by Steve Purcell, is a unit testing -framework inspired by JUnit, which was in turn an adaptation of Kent -Beck's Smalltalk testing framework. See -\url{http://pyunit.sourceforge.net/} for more information about -PyUnit. - -\item The \module{difflib} module contains a class, -\class{SequenceMatcher}, which compares two sequences and computes the -changes required to transform one sequence into the other. For -example, this module can be used to write a tool similar to the Unix -\program{diff} program, and in fact the sample program -\file{Tools/scripts/ndiff.py} demonstrates how to write such a script. - -\item \module{curses.panel}, a wrapper for the panel library, part of -ncurses and of SYSV curses, was contributed by Thomas Gellekum. The -panel library provides windows with the additional feature of depth. -Windows can be moved higher or lower in the depth ordering, and the -panel library figures out where panels overlap and which sections are -visible. - -\item The PyXML package has gone through a few releases since Python -2.0, and Python 2.1 includes an updated version of the \module{xml} -package. Some of the noteworthy changes include support for Expat 1.2 -and later versions, the ability for Expat parsers to handle files in -any encoding supported by Python, and various bugfixes for SAX, DOM, -and the \module{minidom} module. - -\item Ping also contributed another hook for handling uncaught -exceptions. \function{sys.excepthook} can be set to a callable -object. When an exception isn't caught by any -\keyword{try}...\keyword{except} blocks, the exception will be passed -to \function{sys.excepthook}, which can then do whatever it likes. At -the Ninth Python Conference, Ping demonstrated an application for this -hook: printing an extended traceback that not only lists the stack -frames, but also lists the function arguments and the local variables -for each frame. - -\item Various functions in the \module{time} module, such as -\function{asctime()} and \function{localtime()}, require a floating -point argument containing the time in seconds since the epoch. The -most common use of these functions is to work with the current time, -so the floating point argument has been made optional; when a value -isn't provided, the current time will be used. For example, log file -entries usually need a string containing the current time; in Python -2.1, \code{time.asctime()} can be used, instead of the lengthier -\code{time.asctime(time.localtime(time.time()))} that was previously -required. - -This change was proposed and implemented by Thomas Wouters. - -\item The \module{ftplib} module now defaults to retrieving files in -passive mode, because passive mode is more likely to work from behind -a firewall. This request came from the Debian bug tracking system, -since other Debian packages use \module{ftplib} to retrieve files and -then don't work from behind a firewall. It's deemed unlikely that -this will cause problems for anyone, because Netscape defaults to -passive mode and few people complain, but if passive mode is -unsuitable for your application or network setup, call -\method{set_pasv(0)} on FTP objects to disable passive mode. - -\item Support for raw socket access has been added to the -\module{socket} module, contributed by Grant Edwards. - -\item The \module{pstats} module now contains a simple interactive -statistics browser for displaying timing profiles for Python programs, -invoked when the module is run as a script. Contributed by -Eric S.\ Raymond. - -\item A new implementation-dependent function, \function{sys._getframe(\optional{depth})}, -has been added to return a given frame object from the current call stack. -\function{sys._getframe()} returns the frame at the top of the call stack; -if the optional integer argument \var{depth} is supplied, the function returns the frame -that is \var{depth} calls below the top of the stack. For example, \code{sys._getframe(1)} -returns the caller's frame object. - -This function is only present in CPython, not in Jython or the .NET -implementation. Use it for debugging, and resist the temptation to -put it into production code. - - - -\end{itemize} - -%====================================================================== -\section{Other Changes and Fixes} - -There were relatively few smaller changes made in Python 2.1 due to -the shorter release cycle. A search through the CVS change logs turns -up 117 patches applied, and 136 bugs fixed; both figures are likely to -be underestimates. Some of the more notable changes are: - -\begin{itemize} - - -\item A specialized object allocator is now optionally available, that -should be faster than the system \function{malloc()} and have less -memory overhead. The allocator uses C's \function{malloc()} function -to get large pools of memory, and then fulfills smaller memory -requests from these pools. It can be enabled by providing the -\longprogramopt{with-pymalloc} option to the \program{configure} script; see -\file{Objects/obmalloc.c} for the implementation details. - -Authors of C extension modules should test their code with the object -allocator enabled, because some incorrect code may break, causing core -dumps at runtime. There are a bunch of memory allocation functions in -Python's C API that have previously been just aliases for the C -library's \function{malloc()} and \function{free()}, meaning that if -you accidentally called mismatched functions, the error wouldn't be -noticeable. When the object allocator is enabled, these functions -aren't aliases of \function{malloc()} and \function{free()} any more, -and calling the wrong function to free memory will get you a core -dump. For example, if memory was allocated using -\function{PyMem_New()}, it has to be freed using -\function{PyMem_Del()}, not \function{free()}. A few modules included -with Python fell afoul of this and had to be fixed; doubtless there -are more third-party modules that will have the same problem. - -The object allocator was contributed by Vladimir Marangozov. - -\item The speed of line-oriented file I/O has been improved because -people often complain about its lack of speed, and because it's often -been used as a na\"ive benchmark. The \method{readline()} method of -file objects has therefore been rewritten to be much faster. The -exact amount of the speedup will vary from platform to platform -depending on how slow the C library's \function{getc()} was, but is -around 66\%, and potentially much faster on some particular operating -systems. Tim Peters did much of the benchmarking and coding for this -change, motivated by a discussion in comp.lang.python. - -A new module and method for file objects was also added, contributed -by Jeff Epler. The new method, \method{xreadlines()}, is similar to -the existing \function{xrange()} built-in. \function{xreadlines()} -returns an opaque sequence object that only supports being iterated -over, reading a line on every iteration but not reading the entire -file into memory as the existing \method{readlines()} method does. -You'd use it like this: - -\begin{verbatim} -for line in sys.stdin.xreadlines(): - # ... do something for each line ... - ... -\end{verbatim} - -For a fuller discussion of the line I/O changes, see the python-dev -summary for January 1-15, 2001 at -\url{http://www.amk.ca/python/dev/2001-01-1.html}. - -\item A new method, \method{popitem()}, was added to dictionaries to -enable destructively iterating through the contents of a dictionary; -this can be faster for large dictionaries because there's no need to -construct a list containing all the keys or values. -\code{D.popitem()} removes a random \code{(\var{key}, \var{value})} -pair from the dictionary~\code{D} and returns it as a 2-tuple. This -was implemented mostly by Tim Peters and Guido van Rossum, after a -suggestion and preliminary patch by Moshe Zadka. - -\item Modules can now control which names are imported when \code{from -\var{module} import *} is used, by defining an \code{__all__} -attribute containing a list of names that will be imported. One -common complaint is that if the module imports other modules such as -\module{sys} or \module{string}, \code{from \var{module} import *} -will add them to the importing module's namespace. To fix this, -simply list the public names in \code{__all__}: - -\begin{verbatim} -# List public names -__all__ = ['Database', 'open'] -\end{verbatim} - -A stricter version of this patch was first suggested and implemented -by Ben Wolfson, but after some python-dev discussion, a weaker final -version was checked in. - -\item Applying \function{repr()} to strings previously used octal -escapes for non-printable characters; for example, a newline was -\code{'\e 012'}. This was a vestigial trace of Python's C ancestry, but -today octal is of very little practical use. Ka-Ping Yee suggested -using hex escapes instead of octal ones, and using the \code{\e n}, -\code{\e t}, \code{\e r} escapes for the appropriate characters, and -implemented this new formatting. - -\item Syntax errors detected at compile-time can now raise exceptions -containing the filename and line number of the error, a pleasant side -effect of the compiler reorganization done by Jeremy Hylton. - -\item C extensions which import other modules have been changed to use -\function{PyImport_ImportModule()}, which means that they will use any -import hooks that have been installed. This is also encouraged for -third-party extensions that need to import some other module from C -code. - -\item The size of the Unicode character database was shrunk by another -340K thanks to Fredrik Lundh. - -\item Some new ports were contributed: MacOS X (by Steven Majewski), -Cygwin (by Jason Tishler); RISCOS (by Dietmar Schwertberger); Unixware~7 -(by Billy G. Allie). - -\end{itemize} - -And there's the usual list of minor bugfixes, minor memory leaks, -docstring edits, and other tweaks, too lengthy to be worth itemizing; -see the CVS logs for the full details if you want them. - - -%====================================================================== -\section{Acknowledgements} - -The author would like to thank the following people for offering -suggestions on various drafts of this article: Graeme Cross, David -Goodger, Jay Graves, Michael Hudson, Marc-Andr\'e Lemburg, Fredrik -Lundh, Neil Schemenauer, Thomas Wouters. - -\end{document} diff --git a/Include/patchlevel.h b/Include/patchlevel.h index a16559e7b7e9..12c89e0f7e2c 100644 --- a/Include/patchlevel.h +++ b/Include/patchlevel.h @@ -22,11 +22,11 @@ #define PY_MAJOR_VERSION 2 #define PY_MINOR_VERSION 2 #define PY_MICRO_VERSION 0 -#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA -#define PY_RELEASE_SERIAL 1 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL +#define PY_RELEASE_SERIAL 0 /* Version as a string */ -#define PY_VERSION "2.2c1+" +#define PY_VERSION "2.2" /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. Use this for numeric comparisons, e.g. #if PY_VERSION_HEX >= ... */ diff --git a/Lib/compiler/ast.py b/Lib/compiler/ast.py index 23c463b39895..680afeea23d6 100644 --- a/Lib/compiler/ast.py +++ b/Lib/compiler/ast.py @@ -282,6 +282,21 @@ class Module(Node): def __repr__(self): return "Module(%s, %s)" % (repr(self.doc), repr(self.node)) +class Expression(Node): + # Expression is an artifical node class to support "eval" + nodes["expression"] = "Expression" + def __init__(self, node): + self.node = node + + def getChildren(self): + return self.node, + + def getChildNodes(self): + return self.node, + + def __repr__(self): + return "Expression(%s)" % (repr(self.node)) + class UnaryAdd(Node): nodes["unaryadd"] = "UnaryAdd" def __init__(self, expr): diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index f526ae18bbe7..4194d27e25e7 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -34,6 +34,7 @@ EXCEPT = 2 TRY_FINALLY = 3 END_FINALLY = 4 +# XXX this doesn't seem to be used class BlockStack(misc.Stack): __super_init = misc.Stack.__init__ @@ -351,6 +352,13 @@ class CodeGenerator: self.emit('LOAD_CONST', None) self.emit('RETURN_VALUE') + def visitExpression(self, node): + self.set_lineno(node) + self.scopes = self.parseSymbols(node) + self.scope = self.scopes[node] + self.visit(node.node) + self.emit('RETURN_VALUE') + def visitFunction(self, node): self._visitFuncOrLambda(node, isLambda=0) if node.doc: @@ -1158,9 +1166,7 @@ class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator): def __init__(self, tree): self.graph = pyassem.PyFlowGraph("", tree.filename) self.__super_init() - self.set_lineno(tree) walk(tree, self) - self.emit('RETURN_VALUE') def get_module(self): return self @@ -1181,6 +1187,7 @@ class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator): def get_module(self): return self + def visitDiscard(self, node): # XXX Discard means it's an expression. Perhaps this is a bad # name. @@ -1299,7 +1306,6 @@ class ClassCodeGenerator(NestedScopeMixin, AbstractClassCode, CodeGenerator): self.__super_init(klass, scopes, module) self.graph.setFreeVars(self.scope.get_free_vars()) self.graph.setCellVars(self.scope.get_cell_vars()) -## self.graph.setFlag(CO_NESTED) def generateArgList(arglist): """Generate an arg list marking TupleArgs""" diff --git a/Lib/compiler/symbols.py b/Lib/compiler/symbols.py index 200341fa29f8..cd7bcebe0f83 100644 --- a/Lib/compiler/symbols.py +++ b/Lib/compiler/symbols.py @@ -206,6 +206,8 @@ class SymbolVisitor: scope = self.module = self.scopes[node] = ModuleScope() self.visit(node.node, scope) + visitExpression = visitModule + def visitFunction(self, node, parent): parent.add_def(node.name) for n in node.defaults: diff --git a/Lib/compiler/transformer.py b/Lib/compiler/transformer.py index 987556102fac..cd36aaeac9a1 100644 --- a/Lib/compiler/transformer.py +++ b/Lib/compiler/transformer.py @@ -172,7 +172,7 @@ class Transformer: def eval_input(self, nodelist): # from the built-in function input() ### is this sufficient? - return self.com_node(nodelist[0]) + return Expression(self.com_node(nodelist[0])) def funcdef(self, nodelist): # funcdef: 'def' NAME parameters ':' suite diff --git a/Lib/dumbdbm.py b/Lib/dumbdbm.py index 0fd2dad2fc89..920a464d4f3c 100644 --- a/Lib/dumbdbm.py +++ b/Lib/dumbdbm.py @@ -143,9 +143,15 @@ class _Database: return len(self._index) def close(self): + self._commit() self._index = None self._datfile = self._dirfile = self._bakfile = None + def __del__(self): + if self._index is not None: + self._commit() + + def open(file, flag=None, mode=0666): # flag, mode arguments are currently ignored diff --git a/Lib/idlelib/AutoExpand.py b/Lib/idlelib/AutoExpand.py deleted file mode 100644 index 09f34b38d9fb..000000000000 --- a/Lib/idlelib/AutoExpand.py +++ /dev/null @@ -1,91 +0,0 @@ -import string -import re - -###$ event <> -###$ win -###$ unix - -class AutoExpand: - - keydefs = { - '<>': [''], - } - - unix_keydefs = { - '<>': ['', ''], - } - - menudefs = [ - ('edit', [ - ('E_xpand word', '<>'), - ]), - ] - - wordchars = string.letters + string.digits + "_" - - def __init__(self, editwin): - self.text = editwin.text - self.state = None - - def expand_word_event(self, event): - curinsert = self.text.index("insert") - curline = self.text.get("insert linestart", "insert lineend") - if not self.state: - words = self.getwords() - index = 0 - else: - words, index, insert, line = self.state - if insert != curinsert or line != curline: - words = self.getwords() - index = 0 - if not words: - self.text.bell() - return "break" - word = self.getprevword() - self.text.delete("insert - %d chars" % len(word), "insert") - newword = words[index] - index = (index + 1) % len(words) - if index == 0: - self.text.bell() # Warn we cycled around - self.text.insert("insert", newword) - curinsert = self.text.index("insert") - curline = self.text.get("insert linestart", "insert lineend") - self.state = words, index, curinsert, curline - return "break" - - def getwords(self): - word = self.getprevword() - if not word: - return [] - before = self.text.get("1.0", "insert wordstart") - wbefore = re.findall(r"\b" + word + r"\w+\b", before) - del before - after = self.text.get("insert wordend", "end") - wafter = re.findall(r"\b" + word + r"\w+\b", after) - del after - if not wbefore and not wafter: - return [] - words = [] - dict = {} - # search backwards through words before - wbefore.reverse() - for w in wbefore: - if dict.get(w): - continue - words.append(w) - dict[w] = w - # search onwards through words after - for w in wafter: - if dict.get(w): - continue - words.append(w) - dict[w] = w - words.append(word) - return words - - def getprevword(self): - line = self.text.get("insert linestart", "insert") - i = len(line) - while i > 0 and line[i-1] in self.wordchars: - i = i-1 - return line[i:] diff --git a/Lib/idlelib/AutoIndent.py b/Lib/idlelib/AutoIndent.py deleted file mode 100644 index 6d38481a4287..000000000000 --- a/Lib/idlelib/AutoIndent.py +++ /dev/null @@ -1,554 +0,0 @@ -import string -#from Tkinter import TclError -#import tkMessageBox -#import tkSimpleDialog - -###$ event <> -###$ win -###$ win -###$ unix -###$ unix - -###$ event <> -###$ win -###$ unix -###$ unix - -###$ event <> -###$ win -###$ unix -###$ unix - -###$ event <> -###$ win -###$ unix - -###$ event <> -###$ win -###$ unix - -###$ event <> -###$ win -###$ unix - -###$ event <> -###$ win -###$ unix - -import PyParse - -class AutoIndent: - - menudefs = [ - ('format', [ # /s/edit/format dscherer@cmu.edu - None, - ('_Indent region', '<>'), - ('_Dedent region', '<>'), - ('Comment _out region', '<>'), - ('U_ncomment region', '<>'), - ('Tabify region', '<>'), - ('Untabify region', '<>'), - ('Toggle tabs', '<>'), - ('New indent width', '<>'), - ]), - ] - - keydefs = { - '<>': [''], - '<>': ['', ''], - '<>': [''] - } - - windows_keydefs = { - '<>': [''], - '<>': ['', # dscherer@cmu.edu - ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - } - - unix_keydefs = { - '<>': ['', - '', - ''], - '<>': ['', - '', - ''], - '<>': ['', ''], - '<>': ['', ''], - '<>': ['', ''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - } - - # usetabs true -> literal tab characters are used by indent and - # dedent cmds, possibly mixed with spaces if - # indentwidth is not a multiple of tabwidth - # false -> tab characters are converted to spaces by indent - # and dedent cmds, and ditto TAB keystrokes - # indentwidth is the number of characters per logical indent level. - # tabwidth is the display width of a literal tab character. - # CAUTION: telling Tk to use anything other than its default - # tab setting causes it to use an entirely different tabbing algorithm, - # treating tab stops as fixed distances from the left margin. - # Nobody expects this, so for now tabwidth should never be changed. - usetabs = 1 - indentwidth = 4 - tabwidth = 8 # for IDLE use, must remain 8 until Tk is fixed - - # If context_use_ps1 is true, parsing searches back for a ps1 line; - # else searches for a popular (if, def, ...) Python stmt. - context_use_ps1 = 0 - - # When searching backwards for a reliable place to begin parsing, - # first start num_context_lines[0] lines back, then - # num_context_lines[1] lines back if that didn't work, and so on. - # The last value should be huge (larger than the # of lines in a - # conceivable file). - # Making the initial values larger slows things down more often. - num_context_lines = 50, 500, 5000000 - - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text - - def config(self, **options): - for key, value in options.items(): - if key == 'usetabs': - self.usetabs = value - elif key == 'indentwidth': - self.indentwidth = value - elif key == 'tabwidth': - self.tabwidth = value - elif key == 'context_use_ps1': - self.context_use_ps1 = value - else: - raise KeyError, "bad option name: %s" % `key` - - # If ispythonsource and guess are true, guess a good value for - # indentwidth based on file content (if possible), and if - # 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=1): - if guess and ispythonsource: - i = self.guess_indent() - if 2 <= i <= 8: - self.indentwidth = i - if self.indentwidth != self.tabwidth: - self.usetabs = 0 - - self.editwin.set_tabwidth(self.tabwidth) - - def smart_backspace_event(self, event): - text = self.text - first, last = self.editwin.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. - expand, tabwidth = string.expandtabs, self.tabwidth - have = len(expand(chars, tabwidth)) - assert have > 0 - want = int((have - 1) / self.indentwidth) * self.indentwidth - ncharsdeleted = 0 - while 1: - chars = chars[:-1] - ncharsdeleted = ncharsdeleted + 1 - have = len(expand(chars, 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 & return - # indent one level - text = self.text - first, last = self.editwin.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: - if self.usetabs: - pad = '\t' - else: - effective = len(string.expandtabs(prefix, - self.tabwidth)) - n = self.indentwidth - pad = ' ' * (n - effective % n) - text.insert("insert", pad) - text.see("insert") - return "break" - finally: - text.undo_block_stop() - - def newline_and_indent_event(self, event): - text = self.text - first, last = self.editwin.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; just inject - # an empty line at the start - text.insert("insert linestart", '\n') - return "break" - indent = line[:i] - # strip whitespace before insert point - i = 0 - while line and line[-1] in " \t": - 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')) - y = PyParse.Parser(self.indentwidth, self.tabwidth) - 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) - c = y.get_continuation_type() - if c != PyParse.C_NONE: - # The current stmt hasn't ended yet. - if c == PyParse.C_STRING: - # inside a string; 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 " + `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() - - auto_indent = newline_and_indent_event - - # 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. - - def _build_char_in_string_func(self, startindex): - def inner(offset, _startindex=startindex, - _icis=self.editwin.is_char_in_string): - return _icis(_startindex + "+%dc" % offset) - return inner - - 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" - - 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 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] = string.expandtabs(lines[pos], 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.usetabs] + "?", - parent=self.text): - self.usetabs = not self.usetabs - return "break" - - # XXX this isn't bound to anything -- see class 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" - - def change_indentwidth_event(self, event): - new = self.editwin.askinteger( - "Indent width", - "New indent width (1-16)", - parent=self.text, - initialvalue=self.indentwidth, - minvalue=1, - maxvalue=16) - if new and new != self.indentwidth: - self.indentwidth = new - return "break" - - def get_region(self): - text = self.text - first, last = self.editwin.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 = string.split(chars, "\n") - return head, tail, chars, lines - - def set_region(self, head, tail, chars, lines): - text = self.text - newchars = string.join(lines, "\n") - 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") - - # Make string that displays as n leading blanks. - - def _make_blanks(self, n): - if self.usetabs: - ntabs, nspaces = divmod(n, self.tabwidth) - return '\t' * ntabs + ' ' * nspaces - else: - return ' ' * n - - # 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() - - def _asktabwidth(self): - return self.editwin.askinteger( - "Tab width", - "Spaces per tab?", - parent=self.text, - initialvalue=self.tabwidth, - minvalue=1, - maxvalue=16) or self.tabwidth - - # 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 - -class IndentSearcher: - - # .run() chews over the Text widget, looking for a block opener - # and the stmt following it. Returns a pair, - # (line containing block opener, line containing stmt) - # Either or both may be None. - - def __init__(self, text, tabwidth): - self.text = text - self.tabwidth = tabwidth - self.i = self.finished = 0 - self.blkopenline = self.indentedline = None - - def readline(self): - if self.finished: - return "" - i = self.i = self.i + 1 - mark = `i` + ".0" - if self.text.compare(mark, ">=", "end"): - return "" - return self.text.get(mark, mark + " lineend+1c") - - def tokeneater(self, type, token, start, end, line, - INDENT=_tokenize.INDENT, - NAME=_tokenize.NAME, - OPENERS=('class', 'def', 'for', 'if', 'try', 'while')): - if self.finished: - pass - elif type == NAME and token in OPENERS: - self.blkopenline = line - elif type == INDENT and self.blkopenline: - self.indentedline = line - self.finished = 1 - - def run(self): - save_tabsize = _tokenize.tabsize - _tokenize.tabsize = self.tabwidth - try: - try: - _tokenize.tokenize(self.readline, self.tokeneater) - except _tokenize.TokenError: - # since we cut off the tokenizer early, we can trigger - # spurious errors - pass - finally: - _tokenize.tabsize = save_tabsize - return self.blkopenline, self.indentedline diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/Bindings.py deleted file mode 100644 index 1a8374b3fa15..000000000000 --- a/Lib/idlelib/Bindings.py +++ /dev/null @@ -1,76 +0,0 @@ -# This file defines the menu contents and key bindings. Note that -# there is additional configuration information in the EditorWindow -# class (and subclasses): the menus are created there based on the -# menu_specs (class) variable, and menus not created are silently -# skipped by the code here. This makes it possible to define the -# Debug menu here, which is only present in the PythonShell window. - -# changes by dscherer@cmu.edu: -# - Python shell moved to 'Run' menu -# - "Help" renamed to "IDLE Help" to distinguish from Python help. -# The distinction between the environment and the language is dim -# or nonexistent in a novice's mind. -# - Silly advice added - -import sys -import string -#from keydefs import * -from configHandler import idleConf - -menudefs = [ - # underscore prefixes character to underscore - ('file', [ - ('_New window', '<>'), - ('_Open...', '<>'), - ('Open _module...', '<>'), - ('Class _browser', '<>'), - ('_Path browser', '<>'), - None, - ('_Save', '<>'), - ('Save _As...', '<>'), - ('Save Co_py As...', '<>'), - None, - ('_Close', '<>'), - ('E_xit', '<>'), - ]), - ('edit', [ - ('_Undo', '<>'), - ('_Redo', '<>'), - None, - ('Cu_t', '<>'), - ('_Copy', '<>'), - ('_Paste', '<>'), - ('Select _All', '<>'), - ]), - ('run',[ - ('Python shell', '<>'), - ]), - ('debug', [ - ('_Go to file/line', '<>'), - ('_Stack viewer', '<>'), - ('!_Debugger', '<>'), - ('!_Auto-open stack viewer', '<>' ), - ]), -# ('settings', [ -# ('_Configure Idle...', '<>'), -# None, -# ('Revert to _Default Settings', '<>'), -# ]), - ('help', [ - ('_IDLE Help...', '<>'), - ('Python _Documentation...', '<>'), - ('_Advice...', '<>'), - ('View IDLE _Readme...', '<>'), - None, - ('_About IDLE...', '<>'), - ]), -] - -#if sys.platform == 'win32': -# default_keydefs = windows_keydefs -#else: -# default_keydefs = unix_keydefs - -default_keydefs = idleConf.GetKeys(keySetName=None) - -del sys diff --git a/Lib/idlelib/CREDITS.txt b/Lib/idlelib/CREDITS.txt deleted file mode 100644 index 30c2073f0ffa..000000000000 --- a/Lib/idlelib/CREDITS.txt +++ /dev/null @@ -1,17 +0,0 @@ -IDLEfork Credits -================== - -Guido van Rossum, as well as being the creator of the Python language, was -the original creator of IDLE. His great work continues as both a contributor -to, and 'benevolent dictator for life' of Python and IDLE/IDLEfork. - -The main developers who have been so far active on IDLEfork version 0.8.1 -and greater are, Guido van Rossum, Stephen M. Gava and Kurt B. Kaiser. - -The IDLE fork project was initiated and brought up to version 0.7.1 by -David Scherer, Peter Schneider-Kamp and Nicholas Riley. - -There are doubtless others who should be included here, especially those -who may have contributed to IDLE versions prior ot 0.8. Please contact -the IDLEfork coordinator to have yourself included here if you are one of -those I have missed! (contact details at http://idlefork.sourceforge.net) diff --git a/Lib/idlelib/CallTipWindow.py b/Lib/idlelib/CallTipWindow.py deleted file mode 100644 index d253fa5969a9..000000000000 --- a/Lib/idlelib/CallTipWindow.py +++ /dev/null @@ -1,71 +0,0 @@ -# A CallTip window class for Tkinter/IDLE. -# After ToolTip.py, which uses ideas gleaned from PySol - -# Used by the CallTips IDLE extension. -import os -from Tkinter import * - -class CallTip: - - def __init__(self, widget): - self.widget = widget - self.tipwindow = None - self.id = None - self.x = self.y = 0 - - def showtip(self, text): - self.text = text - if self.tipwindow or not self.text: - return - self.widget.see("insert") - x, y, cx, cy = self.widget.bbox("insert") - x = x + self.widget.winfo_rootx() + 2 - y = y + cy + self.widget.winfo_rooty() - self.tipwindow = tw = Toplevel(self.widget) - tw.wm_overrideredirect(1) - tw.wm_geometry("+%d+%d" % (x, y)) - label = Label(tw, text=self.text, justify=LEFT, - background="#ffffe0", relief=SOLID, borderwidth=1, - font = self.widget['font']) - label.pack() - - def hidetip(self): - tw = self.tipwindow - self.tipwindow = None - if tw: - tw.destroy() - - -############################### -# -# Test Code -# -class container: # Conceptually an editor_window - def __init__(self): - root = Tk() - text = self.text = Text(root) - text.pack(side=LEFT, fill=BOTH, expand=1) - text.insert("insert", "string.split") - root.update() - self.calltip = CallTip(text) - - text.event_add("<>", "(") - text.event_add("<>", ")") - text.bind("<>", self.calltip_show) - text.bind("<>", self.calltip_hide) - - text.focus_set() - # root.mainloop() # not in idle - - def calltip_show(self, event): - self.calltip.showtip("Hello world") - - def calltip_hide(self, event): - self.calltip.hidetip() - -def main(): - # Test code - c=container() - -if __name__=='__main__': - main() diff --git a/Lib/idlelib/CallTips.py b/Lib/idlelib/CallTips.py deleted file mode 100644 index 7c5f41c73d55..000000000000 --- a/Lib/idlelib/CallTips.py +++ /dev/null @@ -1,190 +0,0 @@ -# CallTips.py - An IDLE extension that provides "Call Tips" - ie, a floating window that -# displays parameter information as you open parens. - -import string -import sys -import types - -class CallTips: - - menudefs = [ - ] - - keydefs = { - '<>': [''], - '<>': [''], - '<>': [''], - '<>': ['', ''], - } - - windows_keydefs = { - } - - unix_keydefs = { - } - - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text - self.calltip = None - if hasattr(self.text, "make_calltip_window"): - self._make_calltip_window = self.text.make_calltip_window - else: - self._make_calltip_window = self._make_tk_calltip_window - - def close(self): - self._make_calltip_window = None - - # Makes a Tk based calltip window. Used by IDLE, but not Pythonwin. - # See __init__ above for how this is used. - def _make_tk_calltip_window(self): - import CallTipWindow - return CallTipWindow.CallTip(self.text) - - def _remove_calltip_window(self): - if self.calltip: - self.calltip.hidetip() - self.calltip = None - - def paren_open_event(self, event): - self._remove_calltip_window() - arg_text = get_arg_text(self.get_object_at_cursor()) - if arg_text: - self.calltip_start = self.text.index("insert") - self.calltip = self._make_calltip_window() - self.calltip.showtip(arg_text) - return "" #so the event is handled normally. - - def paren_close_event(self, event): - # Now just hides, but later we should check if other - # paren'd expressions remain open. - self._remove_calltip_window() - return "" #so the event is handled normally. - - def check_calltip_cancel_event(self, event): - if self.calltip: - # If we have moved before the start of the calltip, - # or off the calltip line, then cancel the tip. - # (Later need to be smarter about multi-line, etc) - if self.text.compare("insert", "<=", self.calltip_start) or \ - self.text.compare("insert", ">", self.calltip_start + " lineend"): - self._remove_calltip_window() - return "" #so the event is handled normally. - - def calltip_cancel_event(self, event): - self._remove_calltip_window() - return "" #so the event is handled normally. - - def get_object_at_cursor(self, - wordchars="._" + string.uppercase + string.lowercase + string.digits): - # XXX - This needs to be moved to a better place - # so the "." attribute lookup code can also use it. - text = self.text - chars = text.get("insert linestart", "insert") - i = len(chars) - while i and chars[i-1] in wordchars: - i = i-1 - word = chars[i:] - if word: - # How is this for a hack! - import sys, __main__ - namespace = sys.modules.copy() - namespace.update(__main__.__dict__) - try: - return eval(word, namespace) - except: - pass - return None # Can't find an object. - -def _find_constructor(class_ob): - # Given a class object, return a function object used for the - # constructor (ie, __init__() ) or None if we can't find one. - try: - return class_ob.__init__.im_func - except AttributeError: - for base in class_ob.__bases__: - rc = _find_constructor(base) - if rc is not None: return rc - return None - -def get_arg_text(ob): - # Get a string describing the arguments for the given object. - argText = "" - if ob is not None: - argOffset = 0 - if type(ob)==types.ClassType: - # Look for the highest __init__ in the class chain. - fob = _find_constructor(ob) - if fob is None: - fob = lambda: None - else: - argOffset = 1 - elif type(ob)==types.MethodType: - # bit of a hack for methods - turn it into a function - # but we drop the "self" param. - fob = ob.im_func - argOffset = 1 - else: - fob = ob - # Try and build one for Python defined functions - if type(fob) in [types.FunctionType, types.LambdaType]: - try: - realArgs = fob.func_code.co_varnames[argOffset:fob.func_code.co_argcount] - defaults = fob.func_defaults or [] - defaults = list(map(lambda name: "=%s" % name, defaults)) - defaults = [""] * (len(realArgs)-len(defaults)) + defaults - items = map(lambda arg, dflt: arg+dflt, realArgs, defaults) - if fob.func_code.co_flags & 0x4: - items.append("...") - if fob.func_code.co_flags & 0x8: - items.append("***") - argText = string.join(items , ", ") - argText = "(%s)" % argText - except: - pass - # See if we can use the docstring - if hasattr(ob, "__doc__") and ob.__doc__: - pos = string.find(ob.__doc__, "\n") - if pos<0 or pos>70: pos=70 - if argText: argText = argText + "\n" - argText = argText + ob.__doc__[:pos] - - return argText - -################################################# -# -# Test code -# -if __name__=='__main__': - - def t1(): "()" - def t2(a, b=None): "(a, b=None)" - def t3(a, *args): "(a, ...)" - def t4(*args): "(...)" - def t5(a, *args): "(a, ...)" - def t6(a, b=None, *args, **kw): "(a, b=None, ..., ***)" - - class TC: - "(a=None, ...)" - def __init__(self, a=None, *b): "(a=None, ...)" - def t1(self): "()" - def t2(self, a, b=None): "(a, b=None)" - def t3(self, a, *args): "(a, ...)" - def t4(self, *args): "(...)" - def t5(self, a, *args): "(a, ...)" - def t6(self, a, b=None, *args, **kw): "(a, b=None, ..., ***)" - - def test( tests ): - failed=[] - for t in tests: - expected = t.__doc__ + "\n" + t.__doc__ - if get_arg_text(t) != expected: - failed.append(t) - print "%s - expected %s, but got %s" % (t, `expected`, `get_arg_text(t)`) - print "%d of %d tests failed" % (len(failed), len(tests)) - - tc = TC() - tests = t1, t2, t3, t4, t5, t6, \ - TC, tc.t1, tc.t2, tc.t3, tc.t4, tc.t5, tc.t6 - - test(tests) diff --git a/Lib/idlelib/ChangeLog b/Lib/idlelib/ChangeLog deleted file mode 100644 index 899142736546..000000000000 --- a/Lib/idlelib/ChangeLog +++ /dev/null @@ -1,1587 +0,0 @@ -IDLEfork ChangeLog -================== - -2001-07-20 11:35 elguavas - - * README.txt, NEWS.txt: bring up to date for 0.8.1 release - -2001-07-19 16:40 elguavas - - * IDLEFORK.html: replaced by IDLEFORK-index.html - -2001-07-19 16:39 elguavas - - * IDLEFORK-index.html: updated placeholder idlefork homepage - -2001-07-19 14:49 elguavas - - * ChangeLog, EditorWindow.py, INSTALLATION, NEWS.txt, README.txt, - TODO.txt, idlever.py: - minor tidy-ups ready for 0.8.1 alpha tarball release - -2001-07-17 15:12 kbk - - * INSTALLATION, setup.py: INSTALLATION: Remove the coexist.patch - instructions - - **************** setup.py: - - Remove the idles script, add some words on IDLE Fork to the - long_description, and clean up some line spacing. - -2001-07-17 15:01 kbk - - * coexist.patch: Put this in the attic, at least for now... - -2001-07-17 14:59 kbk - - * PyShell.py, idle, idles: Implement idle command interface as - suggested by GvR [idle-dev] 16 July **************** PyShell: Added - functionality: - - usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title] - [arg] ... - - idle file(s) (without options) edit the file(s) - - -c cmd run the command in a shell -d enable the - debugger -i open an interactive shell -i file(s) open a - shell and also an editor window for each file -r script run a file - as a script in a shell -s run $IDLESTARTUP or - $PYTHONSTARTUP before anything else -t title set title of shell - window - - Remaining arguments are applied to the command (-c) or script (-r). - - ****************** idles: Removed the idles script, not needed - - ****************** idle: Removed the IdleConf references, not - required anymore - -2001-07-16 17:08 kbk - - * INSTALLATION, coexist.patch: Added installation instructions. - - Added a patch which modifies idlefork so that it can co-exist with - "official" IDLE in the site-packages directory. This patch is not - necessary if only idlefork IDLE is installed. See INSTALLATION for - further details. - -2001-07-16 15:50 kbk - - * idles: Add a script "idles" which opens a Python Shell window. - - The default behaviour of idlefork idle is to open an editor window - instead of a shell. Complex expressions may be run in a fresh - environment by selecting "run". There are times, however, when a - shell is desired. Though one can be started by "idle -t 'foo'", - this script is more convenient. In addition, a shell and an editor - window can be started in parallel by "idles -e foo.py". - -2001-07-16 15:25 kbk - - * PyShell.py: Call out IDLE Fork in startup message. - -2001-07-16 14:00 kbk - - * PyShell.py, setup.py: Add a script "idles" which opens a Python - Shell window. - - The default behaviour of idlefork idle is to open an editor window - instead of a shell. Complex expressions may be run in a fresh - environment by selecting "run". There are times, however, when a - shell is desired. Though one can be started by "idle -t 'foo'", - this script is more convenient. In addition, a shell and an editor - window can be started in parallel by "idles -e foo.py". - -2001-07-15 03:06 kbk - - * pyclbr.py, tabnanny.py: tabnanny and pyclbr are now found in /Lib - -2001-07-15 02:29 kbk - - * BrowserControl.py: Remove, was retained for 1.5.2 support - -2001-07-14 15:48 kbk - - * setup.py: Installing Idle to site-packages via Distutils does not - copy the Idle help.txt file. - - Ref SF Python Patch 422471 - -2001-07-14 15:26 kbk - - * keydefs.py: py-cvs-2001_07_13 (Rev 1.3) merge - - "Make copy, cut and paste events case insensitive. Reported by - Patrick K. O'Brien on idle-dev. (Should other bindings follow - suit?)" --GvR - -2001-07-14 15:21 kbk - - * idle.py: py-cvs-2001_07_13 (Rev 1.4) merge - - "Move the action of loading the configuration to the IdleConf - module rather than the idle.py script. This has advantages and - disadvantages; the biggest advantage being that we can more easily - have an alternative main program." --GvR - -2001-07-14 15:18 kbk - - * extend.txt: py-cvs-2001_07_13 (Rev 1.4) merge - - "Quick update to the extension mechanism (extend.py is gone, long - live config.txt)" --GvR - -2001-07-14 15:15 kbk - - * StackViewer.py: py-cvs-2001_07_13 (Rev 1.16) merge - - "Refactored, with some future plans in mind. This now uses the new - gotofileline() method defined in FileList.py" --GvR - -2001-07-14 15:10 kbk - - * PyShell.py: py-cvs-2001_07_13 (Rev 1.34) merge - - "Amazing. A very subtle change in policy in descr-branch actually - found a bug here. Here's the deal: Class PyShell derives from - class OutputWindow. Method PyShell.close() wants to invoke its - parent method, but because PyShell long ago was inherited from - class PyShellEditorWindow, it invokes - PyShelEditorWindow.close(self). Now, class PyShellEditorWindow - itself derives from class OutputWindow, and inherits the close() - method from there without overriding it. Under the old rules, - PyShellEditorWindow.close would return an unbound method restricted - to the class that defined the implementation of close(), which was - OutputWindow.close. Under the new rules, the unbound method is - restricted to the class whose method was requested, that is - PyShellEditorWindow, and this was correctly trapped as an error." - --GvR - -2001-07-14 14:59 kbk - - * PyParse.py: py-cvs-2001_07_13 (Rel 1.9) merge - - "Taught IDLE's autoident parser that "yield" is a keyword that - begins a stmt. Along w/ the preceding change to keyword.py, making - all this work w/ a future-stmt just looks harder and harder." - --tim_one - - (From Rel 1.8: "Hack to make this still work with Python 1.5.2. - ;-( " --fdrake) - -2001-07-14 14:51 kbk - - * IdleConf.py: py-cvs-2001_07_13 (Rel 1.7) merge - - "Move the action of loading the configuration to the IdleConf - module rather than the idle.py script. This has advantages and - disadvantages; the biggest advantage being that we can more easily - have an alternative main program." --GvR - -2001-07-14 14:45 kbk - - * FileList.py: py-cvs-2000_07_13 (Rev 1.9) merge - - "Delete goodname() method, which is unused. Add gotofileline(), a - convenience method which I intend to use in a variant. Rename - test() to _test()." --GvR - - This was an interesting merge. The join completely missed removing - goodname(), which was adjacent, but outside of, a small conflict. - I only caught it by comparing the 1.1.3.2/1.1.3.3 diff. CVS ain't - infallible. - -2001-07-14 13:58 kbk - - * EditorWindow.py: py-cvs-2000_07_13 (Rev 1.38) merge "Remove - legacy support for the BrowserControl module; the webbrowser module - has been included since Python 2.0, and that is the preferred - interface." --fdrake - -2001-07-14 13:32 kbk - - * EditorWindow.py, FileList.py, IdleConf.py, PyParse.py, - PyShell.py, StackViewer.py, extend.txt, idle.py, keydefs.py: Import - the 2001 July 13 23:59 GMT version of Python CVS IDLE on the - existing 1.1.3 vendor branch named py-cvs-vendor-branch. Release - tag is py-cvs-2001_07_13. - -2001-07-14 12:02 kbk - - * Icons/python.gif: py-cvs-rel2_1 (Rev 1.2) merge Copied py-cvs rev - 1.2 changed file to idlefork MAIN - -2001-07-14 11:58 kbk - - * Icons/minusnode.gif: py-cvs-rel2_1 (Rev 1.2) merge Copied py-cvs - 1.2 changed file to idlefork MAIN - -2001-07-14 11:23 kbk - - * ScrolledList.py: py-cvs-rel2_1 (rev 1.5) merge - whitespace - normalization - -2001-07-14 11:20 kbk - - * Separator.py: py-cvs-rel2_1 (Rev 1.3) merge - whitespace - normalization - -2001-07-14 11:16 kbk - - * StackViewer.py: py-cvs-rel2_1 (Rev 1.15) merge - whitespace - normalization - -2001-07-14 11:14 kbk - - * ToolTip.py: py-cvs-rel2_1 (Rev 1.2) merge - whitespace - normalization - -2001-07-14 10:13 kbk - - * PyShell.py: cvs-py-rel2_1 (Rev 1.29 - 1.33) merge - - Merged the following py-cvs revs without conflict: 1.29 Reduce - copyright text output at startup 1.30 Delay setting sys.args until - Tkinter is fully initialized 1.31 Whitespace normalization 1.32 - Turn syntax warning into error when interactive 1.33 Fix warning - initialization bug - - Note that module is extensively modified wrt py-cvs - -2001-07-14 06:33 kbk - - * PyParse.py: py-cvs-rel2_1 (Rev 1.6 - 1.8) merge Fix autoindent - bug and deflect Unicode from text.get() - -2001-07-14 06:00 kbk - - * Percolator.py: py-cvs-rel2_1 (Rev 1.3) "move "from Tkinter import - *" to module level" --jhylton - -2001-07-14 05:57 kbk - - * PathBrowser.py: py-cvs-rel2_1 (Rev 1.6) merge - whitespace - normalization - -2001-07-14 05:49 kbk - - * ParenMatch.py: cvs-py-rel2_1 (Rev 1.5) merge - whitespace - normalization - -2001-07-14 03:57 kbk - - * ObjectBrowser.py: py-cvs-rel2_1 (Rev 1.3) merge "Make the test - program work outside IDLE." -- GvR - -2001-07-14 03:52 kbk - - * MultiStatusBar.py: py-cvs-rel2_1 (Rev 1.2) merge - whitespace - normalization - -2001-07-14 03:44 kbk - - * MultiScrolledLists.py: py-cvs-rel2_1 (Rev 1.2) merge - whitespace - normalization - -2001-07-14 03:40 kbk - - * IdleHistory.py: py-cvs-rel2_1 (Rev 1.4) merge - whitespace - normalization - -2001-07-14 03:38 kbk - - * IdleConf.py: py-cvs-rel2_1 (Rev 1.6) merge - whitespace - normalization - -2001-07-13 14:18 kbk - - * IOBinding.py: py-cvs-rel2_1 (Rev 1.4) merge - move "import *" to - module level - -2001-07-13 14:12 kbk - - * FormatParagraph.py: py-cvs-rel2_1 (Rev 1.9) merge - whitespace - normalization - -2001-07-13 14:07 kbk - - * FileList.py: py-cvs-rel2_1 (Rev 1.8) merge - whitespace - normalization - -2001-07-13 13:35 kbk - - * EditorWindow.py: py-cvs-rel2_1 (Rev 1.33 - 1.37) merge - - VP IDLE version depended on VP's ExecBinding.py and spawn.py to get - the path to the Windows Doc directory (relative to python.exe). - Removed this conflicting code in favor of py-cvs updates which on - Windows use a hard coded path relative to the location of this - module. py-cvs updates include support for webbrowser.py. Module - still has BrowserControl.py for 1.5.2 support. - - At this point, the differences wrt py-cvs relate to menu - functionality. - -2001-07-13 11:30 kbk - - * ConfigParser.py: py-cvs-rel2_1 merge - Remove, lives in /Lib - -2001-07-13 10:10 kbk - - * Delegator.py: py-cvs-rel2_1 (Rev 1.3) merge - whitespace - normalization - -2001-07-13 10:07 kbk - - * Debugger.py: py-cvs-rel2_1 (Rev 1.15) merge - whitespace - normalization - -2001-07-13 10:04 kbk - - * ColorDelegator.py: py-cvs-rel2_1 (Rev 1.11 and 1.12) merge - Colorize "as" after "import" / use DEBUG instead of __debug__ - -2001-07-13 09:54 kbk - - * ClassBrowser.py: py-cvs-rel2_1 (Rev 1.12) merge - whitespace - normalization - -2001-07-13 09:41 kbk - - * BrowserControl.py: py-cvs-rel2_1 (Rev 1.1) merge - New File - - Force HEAD to trunk with -f Note: browser.py was renamed - BrowserControl.py 10 May 2000. It provides a collection of classes - and convenience functions to control external browsers "for 1.5.2 - support". It was removed from py-cvs 18 April 2001. - -2001-07-13 09:10 kbk - - * CallTips.py: py-cvs-rel2_1 (Rev 1.8) merge - whitespace - normalization - -2001-07-13 08:26 kbk - - * CallTipWindow.py: py-cvs-rel2_1 (Rev 1.3) merge - whitespace - normalization - -2001-07-13 08:13 kbk - - * AutoExpand.py: py-cvs-rel1_2 (Rev 1.4) merge, "Add Alt-slash to - Unix keydefs (I somehow need it on RH 6.2). Get rid of assignment - to unused self.text.wordlist." --GvR - -2001-07-12 16:54 elguavas - - * ReplaceDialog.py: py-cvs merge, python 1.5.2 compatability - -2001-07-12 16:46 elguavas - - * ScriptBinding.py: py-cvs merge, better error dialog - -2001-07-12 16:38 elguavas - - * TODO.txt: py-cvs merge, additions - -2001-07-12 15:35 elguavas - - * WindowList.py: py-cvs merge, correct indentation - -2001-07-12 15:24 elguavas - - * config.txt: py-cvs merge, correct typo - -2001-07-12 15:21 elguavas - - * help.txt: py-cvs merge, update colour changing info - -2001-07-12 14:51 elguavas - - * idle.py: py-cvs merge, idle_dir loading changed - -2001-07-12 14:44 elguavas - - * idlever.py: py-cvs merge, version update - -2001-07-11 12:53 kbk - - * BrowserControl.py: Initial revision - -2001-07-11 12:53 kbk - - * AutoExpand.py, BrowserControl.py, CallTipWindow.py, CallTips.py, - ClassBrowser.py, ColorDelegator.py, Debugger.py, Delegator.py, - EditorWindow.py, FileList.py, FormatParagraph.py, IOBinding.py, - IdleConf.py, IdleHistory.py, MultiScrolledLists.py, - MultiStatusBar.py, ObjectBrowser.py, OutputWindow.py, - ParenMatch.py, PathBrowser.py, Percolator.py, PyParse.py, - PyShell.py, RemoteInterp.py, ReplaceDialog.py, ScriptBinding.py, - ScrolledList.py, Separator.py, StackViewer.py, TODO.txt, - ToolTip.py, WindowList.py, config.txt, help.txt, idle, idle.bat, - idle.py, idlever.py, setup.py, Icons/minusnode.gif, - Icons/python.gif: Import the release 2.1 version of Python CVS IDLE - on the existing 1.1.3 vendor branch named py-cvs-vendor-branch, - with release tag py-cvs-rel2_1. - -2001-07-11 12:34 kbk - - * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py, - CallTips.py, ChangeLog, ClassBrowser.py, ColorDelegator.py, - Debugger.py, Delegator.py, EditorWindow.py, FileList.py, - FormatParagraph.py, FrameViewer.py, GrepDialog.py, IOBinding.py, - IdleConf.py, IdleHistory.py, MultiScrolledLists.py, - MultiStatusBar.py, NEWS.txt, ObjectBrowser.py, OldStackViewer.py, - OutputWindow.py, ParenMatch.py, PathBrowser.py, Percolator.py, - PyParse.py, PyShell.py, README.txt, RemoteInterp.py, - ReplaceDialog.py, ScriptBinding.py, ScrolledList.py, - SearchBinding.py, SearchDialog.py, SearchDialogBase.py, - SearchEngine.py, Separator.py, StackViewer.py, TODO.txt, - ToolTip.py, TreeWidget.py, UndoDelegator.py, WidgetRedirector.py, - WindowList.py, ZoomHeight.py, __init__.py, config-unix.txt, - config-win.txt, config.txt, eventparse.py, extend.txt, help.txt, - idle.bat, idle.py, idle.pyw, idlever.py, keydefs.py, pyclbr.py, - tabnanny.py, testcode.py, Icons/folder.gif, Icons/minusnode.gif, - Icons/openfolder.gif, Icons/plusnode.gif, Icons/python.gif, - Icons/tk.gif: Import the 9 March 2000 version of Python CVS IDLE as - 1.1.3 vendor branch named py-cvs-vendor-branch. - -2001-07-04 13:43 kbk - - * Icons/: folder.gif, minusnode.gif, openfolder.gif, plusnode.gif, - python.gif, tk.gif: Null commit with -f option to force an uprev - and put HEADs firmly on the trunk. - -2001-07-04 13:15 kbk - - * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py, - CallTips.py, ChangeLog, ClassBrowser.py, ColorDelegator.py, - ConfigParser.py, Debugger.py, Delegator.py, EditorWindow.py, - ExecBinding.py, FileList.py, FormatParagraph.py, FrameViewer.py, - GrepDialog.py, IDLEFORK.html, IOBinding.py, IdleConf.py, - IdleHistory.py, MultiScrolledLists.py, MultiStatusBar.py, NEWS.txt, - ObjectBrowser.py, OldStackViewer.py, OutputWindow.py, - ParenMatch.py, PathBrowser.py, Percolator.py, PyParse.py, - PyShell.py, README.txt, Remote.py, RemoteInterp.py, - ReplaceDialog.py, ScriptBinding.py, ScrolledList.py, - SearchBinding.py, SearchDialog.py, SearchDialogBase.py, - SearchEngine.py, Separator.py, StackViewer.py, TODO.txt, - ToolTip.py, TreeWidget.py, UndoDelegator.py, WidgetRedirector.py, - WindowList.py, ZoomHeight.py, __init__.py, config-unix.txt, - config-win.txt, config.txt, eventparse.py, extend.txt, help.txt, - idle, idle.bat, idle.py, idle.pyw, idlever.py, keydefs.py, - loader.py, protocol.py, pyclbr.py, setup.py, spawn.py, tabnanny.py, - testcode.py: Null commit with -f option to force an uprev and put - HEADs firmly on the trunk. - -2001-06-27 10:24 elguavas - - * IDLEFORK.html: updated contact details - -2001-06-25 17:23 elguavas - - * idle, RemoteInterp.py, setup.py: Initial revision - -2001-06-25 17:23 elguavas - - * idle, RemoteInterp.py, setup.py: import current python cvs idle - as a vendor branch - -2001-06-24 15:10 elguavas - - * IDLEFORK.html: tiny change to test new syncmail setup - -2001-06-24 14:41 elguavas - - * IDLEFORK.html: change to new developer contact, also a test - commit for new syncmail setup - -2001-06-23 18:15 elguavas - - * IDLEFORK.html: tiny test update for revitalised idle-fork - -2000-09-24 17:29 nriley - - * protocol.py: Fixes for Python 1.6 compatibility - socket bind and - connect get a tuple instead two arguments. - -2000-09-24 17:28 nriley - - * spawn.py: Change for Python 1.6 compatibility - UNIX's 'os' - module defines 'spawnv' now, so we check for 'fork' first. - -2000-08-15 22:51 nowonder - - * IDLEFORK.html: - corrected email address - -2000-08-15 22:47 nowonder - - * IDLEFORK.html: - added .html file for http://idlefork.sourceforge.net - -2000-08-15 11:13 dscherer - - * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py, - CallTips.py, __init__.py, ChangeLog, ClassBrowser.py, - ColorDelegator.py, ConfigParser.py, Debugger.py, Delegator.py, - FileList.py, FormatParagraph.py, FrameViewer.py, GrepDialog.py, - IOBinding.py, IdleConf.py, IdleHistory.py, MultiScrolledLists.py, - MultiStatusBar.py, NEWS.txt, ObjectBrowser.py, OldStackViewer.py, - OutputWindow.py, ParenMatch.py, PathBrowser.py, Percolator.py, - PyParse.py, PyShell.py, README.txt, ReplaceDialog.py, - ScriptBinding.py, ScrolledList.py, SearchBinding.py, - SearchDialog.py, SearchDialogBase.py, SearchEngine.py, - Separator.py, StackViewer.py, TODO.txt, ToolTip.py, TreeWidget.py, - UndoDelegator.py, WidgetRedirector.py, WindowList.py, help.txt, - ZoomHeight.py, config-unix.txt, config-win.txt, config.txt, - eventparse.py, extend.txt, idle.bat, idle.py, idle.pyw, idlever.py, - keydefs.py, loader.py, pyclbr.py, tabnanny.py, testcode.py, - EditorWindow.py, ExecBinding.py, Remote.py, protocol.py, spawn.py, - Icons/folder.gif, Icons/minusnode.gif, Icons/openfolder.gif, - Icons/plusnode.gif, Icons/python.gif, Icons/tk.gif: Initial - revision - -2000-08-15 11:13 dscherer - - * AutoExpand.py, AutoIndent.py, Bindings.py, CallTipWindow.py, - CallTips.py, __init__.py, ChangeLog, ClassBrowser.py, - ColorDelegator.py, ConfigParser.py, Debugger.py, Delegator.py, - FileList.py, FormatParagraph.py, FrameViewer.py, GrepDialog.py, - IOBinding.py, IdleConf.py, IdleHistory.py, MultiScrolledLists.py, - MultiStatusBar.py, NEWS.txt, ObjectBrowser.py, OldStackViewer.py, - OutputWindow.py, ParenMatch.py, PathBrowser.py, Percolator.py, - PyParse.py, PyShell.py, README.txt, ReplaceDialog.py, - ScriptBinding.py, ScrolledList.py, SearchBinding.py, - SearchDialog.py, SearchDialogBase.py, SearchEngine.py, - Separator.py, StackViewer.py, TODO.txt, ToolTip.py, TreeWidget.py, - UndoDelegator.py, WidgetRedirector.py, WindowList.py, help.txt, - ZoomHeight.py, config-unix.txt, config-win.txt, config.txt, - eventparse.py, extend.txt, idle.bat, idle.py, idle.pyw, idlever.py, - keydefs.py, loader.py, pyclbr.py, tabnanny.py, testcode.py, - EditorWindow.py, ExecBinding.py, Remote.py, protocol.py, spawn.py, - Icons/folder.gif, Icons/minusnode.gif, Icons/openfolder.gif, - Icons/plusnode.gif, Icons/python.gif, Icons/tk.gif: Modified IDLE - from VPython 0.2 - - -original IDLE ChangeLog: -======================== - -Tue Feb 15 18:08:19 2000 Guido van Rossum - - * NEWS.txt: Notice status bar and stack viewer. - - * EditorWindow.py: Support for Moshe's status bar. - - * MultiStatusBar.py: Status bar code -- by Moshe Zadka. - - * OldStackViewer.py: - Adding the old stack viewer implementation back, for the debugger. - - * StackViewer.py: New stack viewer, uses a tree widget. - (XXX: the debugger doesn't yet use this.) - - * WindowList.py: - Correct a typo and remove an unqualified except that was hiding the error. - - * ClassBrowser.py: Add an XXX comment about the ClassBrowser AIP. - - * ChangeLog: Updated change log. - - * NEWS.txt: News update. Probably incomplete; what else is new? - - * README.txt: - Updated for pending IDLE 0.5 release (still very rough -- just getting - it out in a more convenient format than CVS). - - * TODO.txt: Tiny addition. - -Thu Sep 9 14:16:02 1999 Guido van Rossum - - * TODO.txt: A few new TODO entries. - -Thu Aug 26 23:06:22 1999 Guido van Rossum - - * Bindings.py: Add Python Documentation entry to Help menu. - - * EditorWindow.py: - Find the help.txt file relative to __file__ or ".", not in sys.path. - (Suggested by Moshe Zadka, but implemented differently.) - - Add <> event which, on Unix, brings up Netscape pointing - to http://www.python.doc/current/ (a local copy would be nice but its - location can't be predicted). Windows solution TBD. - -Wed Aug 11 14:55:43 1999 Guido van Rossum - - * TreeWidget.py: - Moshe noticed an inconsistency in his comment, so I'm rephrasing it to - be clearer. - - * TreeWidget.py: - Patch inspired by Moshe Zadka to search for the Icons directory in the - same directory as __file__, rather than searching for it along sys.path. - This works better when idle is a package. - -Thu Jul 15 13:11:02 1999 Guido van Rossum - - * TODO.txt: New wishes. - -Sat Jul 10 13:17:35 1999 Guido van Rossum - - * IdlePrefs.py: - Make the color for stderr red (i.e. the standard warning/danger/stop - color) rather than green. Suggested by Sam Schulenburg. - -Fri Jun 25 17:26:34 1999 Guido van Rossum - - * PyShell.py: Close debugger when closing. This may break a cycle. - - * Debugger.py: Break cycle on close. - - * ClassBrowser.py: Destroy the tree when closing. - - * TreeWidget.py: Add destroy() method to recursively destroy a tree. - - * PyShell.py: Extend _close() to break cycles. - Break some other cycles too (and destroy the root when done). - - * EditorWindow.py: - Add _close() method that does the actual cleanup (close() asks the - user what they want first if there's unsaved stuff, and may cancel). - It closes more than before. - - Add unload_extensions() method to unload all extensions; called from - _close(). It calls an extension's close() method if it has one. - - * Percolator.py: Add close() method that breaks cycles. - - * WidgetRedirector.py: Add unregister() method. - Unregister everything at closing. - Don't call close() in __del__, rely on explicit call to close(). - - * IOBinding.py, FormatParagraph.py, CallTips.py: - Add close() method that breaks a cycle. - -Fri Jun 11 15:03:00 1999 Guido van Rossum - - * AutoIndent.py, EditorWindow.py, FormatParagraph.py: - Tim Peters smart.patch: - - EditorWindow.py: - - + Added get_tabwidth & set_tabwidth "virtual text" methods, that get/set the - widget's view of what a tab means. - - + Moved TK_TABWIDTH_DEFAULT here from AutoIndent. - - + Renamed Mark's get_selection_index to get_selection_indices (sorry, Mark, - but the name was plain wrong ). - - FormatParagraph.py: renamed use of get_selection_index. - - AutoIndent.py: - - + Moved TK_TABWIDTH_DEFAULT to EditorWindow. - - + Rewrote set_indentation_params to use new VTW get/set_tabwidth methods. - - + Changed smart_backspace_event to delete whitespace back to closest - preceding virtual tab stop or real character (note that this may require - inserting characters if backspacing over a tab!). - - + Nuked almost references to the selection tag, in favor of using - get_selection_indices. The sole exception is in set_region, for which no - "set_selection" abstraction has yet been agreed upon. - - + Had too much fun using the spiffy new features of the format-paragraph - cmd. - -Thu Jun 10 17:48:02 1999 Guido van Rossum - - * FormatParagraph.py: - Code by Mark Hammond to format paragraphs embedded in comments. - Read the comments (which I reformatted using the new feature :-) - for some limitations. - - * EditorWindow.py: - Added abstraction get_selection_index() (Mark Hammond). Also - reformatted some comment blocks to show off a cool feature I'm about - to check in next. - - * ClassBrowser.py: - Adapt to the new pyclbr's support of listing top-level functions. If - this functionality is not present (e.g. when used with a vintage - Python 1.5.2 installation) top-level functions are not listed. - - (Hmm... Any distribution of IDLE 0.5 should probably include a copy - of the new pyclbr.py!) - - * AutoIndent.py: - Fix off-by-one error in Tim's recent change to comment_region(): the - list of lines returned by get_region() contains an empty line at the - end representing the start of the next line, and this shouldn't be - commented out! - - * CallTips.py: - Mark Hammond writes: Here is another change that allows it to work for - class creation - tries to locate an __init__ function. Also updated - the test code to reflect your new "***" change. - - * CallTipWindow.py: - Mark Hammond writes: Tim's suggestion of copying the font for the - CallTipWindow from the text control makes sense, and actually makes - the control look better IMO. - -Wed Jun 9 20:34:57 1999 Guido van Rossum - - * CallTips.py: - Append "..." if the appropriate flag (for varargs) in co_flags is set. - Ditto "***" for kwargs. - -Tue Jun 8 13:06:07 1999 Guido van Rossum - - * ReplaceDialog.py: - Hmm... Tim didn't turn "replace all" into a single undo block. - I think I like it better if it os, so here. - - * ReplaceDialog.py: Tim Peters: made replacement atomic for undo/redo. - - * AutoIndent.py: Tim Peters: - - + Set usetabs=1. Editing pyclbr.py was driving me nuts <0.6 wink>. - usetabs=1 is the Emacs pymode default too, and thanks to indentwidth != - tabwidth magical usetabs disabling, new files are still created with tabs - turned off. The only implication is that if you open a file whose first - indent is a single tab, IDLE will now magically use tabs for that file (and - set indentwidth to 8). Note that the whole scheme doesn't work right for - PythonWin, though, since Windows users typically set tabwidth to 4; Mark - probably has to hide the IDLE algorithm from them (which he already knows). - - + Changed comment_region_event to stick "##" in front of every line. The - "holes" previously left on blank lines were visually confusing (made it - needlessly hard to figure out what to uncomment later). - -Mon Jun 7 15:38:40 1999 Guido van Rossum - - * TreeWidget.py, ObjectBrowser.py: - Remove unnecessary reference to pyclbr from test() code. - - * PyParse.py: Tim Peters: - - Smarter logic for finding a parse synch point. - - Does a half to a fifth the work in normal cases; don't notice the speedup, - but makes more breathing room for other extensions. - - Speeds terrible cases by at least a factor of 10. "Terrible" == e.g. you put - """ at the start of Tkinter.py, undo it, zoom to the bottom, and start - typing in code. Used to take about 8 seconds for ENTER to respond, now some - large fraction of a second. The new code gets indented correctly, despite - that it all remains "string colored" until the colorizer catches up (after - which, ENTER appears instantaneous again). - -Fri Jun 4 19:21:19 1999 Guido van Rossum - - * extend.py: Might as well enable CallTips by default. - If there are too many complaints I'll remove it again or fix it. - -Thu Jun 3 14:32:16 1999 Guido van Rossum - - * AutoIndent.py, EditorWindow.py, PyParse.py: - New offerings by Tim Peters; he writes: - - IDLE is now the first Python editor in the Universe not confused by my - doctest.py . - - As threatened, this defines IDLE's is_char_in_string function as a - method of EditorWindow. You just need to define one similarly in - whatever it is you pass as editwin to AutoIndent; looking at the - EditorWindow.py part of the patch should make this clear. - - * GrepDialog.py: Enclose pattern in quotes in status message. - - * CallTips.py: - Mark Hammond fixed some comments and improved the way the tip text is - constructed. - -Wed Jun 2 18:18:57 1999 Guido van Rossum - - * CallTips.py: - My fix to Mark's code: restore the universal check on . - Always cancel on or . - - * CallTips.py: - A version that Mark Hammond posted to the newsgroup. Has some newer - stuff for getting the tip. Had to fix the Key-( and Key-) events - for Unix. Will have to re-apply my patch for catching KeyRelease and - ButtonRelease events. - - * CallTipWindow.py, CallTips.py: - Call tips by Mark Hammond (plus tiny fix by me.) - - * IdleHistory.py: - Changes by Mark Hammond: (1) support optional output_sep argument to - the constructor so he can eliminate the sys.ps2 that PythonWin leaves - in the source; (2) remove duplicate history items. - - * AutoIndent.py: - Changes by Mark Hammond to allow using IDLE extensions in PythonWin as - well: make three dialog routines instance variables. - - * EditorWindow.py: - Change by Mark Hammond to allow using IDLE extensions in PythonWin as - well: make three dialog routines instance variables. - -Tue Jun 1 20:06:44 1999 Guido van Rossum - - * AutoIndent.py: Hah! A fix of my own to Tim's code! - Unix bindings for <> and <> were - missing, and somehow that meant the events were never generated, - even though they were in the menu. The new Unix bindings are now - the same as the Windows bindings (M-t and M-u). - - * AutoIndent.py, PyParse.py, PyShell.py: Tim Peters again: - - The new version (attached) is fast enough all the time in every real module - I have . You can make it slow by, e.g., creating an open list with - 5,000 90-character identifiers (+ trailing comma) each on its own line, then - adding an item to the end -- but that still consumes less than a second on - my P5-166. Response time in real code appears instantaneous. - - Fixed some bugs. - - New feature: when hitting ENTER and the cursor is beyond the line's leading - indentation, whitespace is removed on both sides of the cursor; before - whitespace was removed only on the left; e.g., assuming the cursor is - between the comma and the space: - - def something(arg1, arg2): - ^ cursor to the left of here, and hit ENTER - arg2): # new line used to end up here - arg2): # but now lines up the way you expect - - New hack: AutoIndent has grown a context_use_ps1 Boolean config option, - defaulting to 0 (false) and set to 1 (only) by PyShell. Reason: handling - the fancy stuff requires looking backward for a parsing synch point; ps1 - lines are the only sensible thing to look for in a shell window, but are a - bad thing to look for in a file window (ps1 lines show up in my module - docstrings often). PythonWin's shell should set this true too. - - Persistent problem: strings containing def/class can still screw things up - completely. No improvement. Simplest workaround is on the user's head, and - consists of inserting e.g. - - def _(): pass - - (or any other def/class) after the end of the multiline string that's - screwing them up. This is especially irksome because IDLE's syntax coloring - is *not* confused, so when this happens the colors don't match the - indentation behavior they see. - - * AutoIndent.py: Tim Peters again: - - [Tim, after adding some bracket smarts to AutoIndent.py] - > ... - > What it can't possibly do without reparsing large gobs of text is - > suggest a reasonable indent level after you've *closed* a bracket - > left open on some previous line. - > ... - - The attached can, and actually fast enough to use -- most of the time. The - code is tricky beyond belief to achieve that, but it works so far; e.g., - - return len(string.expandtabs(str[self.stmt_start : - ^ indents to caret - i], - ^ indents to caret - self.tabwidth)) + 1 - ^ indents to caret - - It's about as smart as pymode now, wrt both bracket and backslash - continuation rules. It does require reparsing large gobs of text, and if it - happens to find something that looks like a "def" or "class" or sys.ps1 - buried in a multiline string, but didn't suck up enough preceding text to - see the start of the string, it's completely hosed. I can't repair that -- - it's just too slow to reparse from the start of the file all the time. - - AutoIndent has grown a new num_context_lines tuple attribute that controls - how far to look back, and-- like other params --this could/should be made - user-overridable at startup and per-file on the fly. - - * PyParse.py: New file by Tim Peters: - - One new file in the attached, PyParse.py. The LineStudier (whatever it was - called ) class was removed from AutoIndent; PyParse subsumes its - functionality. - - * AutoIndent.py: Tim Peters keeps revising this module (more to come): - - Removed "New tabwidth" menu binding. - - Added "a tab means how many spaces?" dialog to block tabify and untabify. I - think prompting for this is good now: they're usually at-most-once-per-file - commands, and IDLE can't let them change tabwidth from the Tk default - anymore, so IDLE can no longer presume to have any idea what a tab means. - - Irony: for the purpose of keeping comments aligned via tabs, Tk's - non-default approach is much nicer than the Emacs/Notepad/Codewright/vi/etc - approach. - - * EditorWindow.py: - 1. Catch NameError on import (could be raised by case mismatch on Windows). - 2. No longer need to reset pyclbr cache and show watch cursor when calling - ClassBrowser -- the ClassBrowser takes care of pyclbr and the TreeWidget - takes care of the watch cursor. - 3. Reset the focus to the current window after error message about class - browser on buffer without filename. - - * Icons/minusnode.gif, Icons/plusnode.gif: Missed a few. - - * ClassBrowser.py, PathBrowser.py: Rewritten based on TreeWidget.py - - * ObjectBrowser.py: Object browser, based on TreeWidget.py. - - * TreeWidget.py: Tree widget done right. - - * ToolTip.py: As yet unused code for tool tips. - - * ScriptBinding.py: - Ensure sys.argv[0] is the script name on Run Script. - - * ZoomHeight.py: Move zoom height functionality to separate function. - - * Icons/folder.gif, Icons/openfolder.gif, Icons/python.gif, Icons/tk.gif: - A few icons used by ../TreeWidget.py and its callers. - - * AutoIndent.py: New version by Tim Peters improves block opening test. - -Fri May 21 04:46:17 1999 Guido van Rossum - - * Attic/History.py, PyShell.py: Rename History to IdleHistory. - Add isatty() to pseudo files. - - * StackViewer.py: Make initial stack viewer wider - - * TODO.txt: New wishes - - * AutoIndent.py, EditorWindow.py, PyShell.py: - Much improved autoindent and handling of tabs, - by Tim Peters. - -Mon May 3 15:49:52 1999 Guido van Rossum - - * AutoIndent.py, EditorWindow.py, FormatParagraph.py, UndoDelegator.py: - Tim Peters writes: - - I'm still unsure, but couldn't stand the virtual event trickery so tried a - different sin (adding undo_block_start/stop methods to the Text instance in - EditorWindow.py). Like it or not, it's efficient and works . Better - idea? - - Give the attached a whirl. Even if you hate the implementation, I think - you'll like the results. Think I caught all the "block edit" cmds, - including Format Paragraph, plus subtler ones involving smart indents and - backspacing. - - * WidgetRedirector.py: Tim Peters writes: - - [W]hile trying to dope out how redirection works, stumbled into two - possible glitches. In the first, it doesn't appear to make sense to try to - rename a command that's already been destroyed; in the second, the name - "previous" doesn't really bring to mind "ignore the previous value" . - -Fri Apr 30 19:39:25 1999 Guido van Rossum - - * __init__.py: Support for using idle as a package. - - * PathBrowser.py: - Avoid listing files more than once (e.g. foomodule.so has two hits: - once for foo + module.so, once for foomodule + .so). - -Mon Apr 26 22:20:38 1999 Guido van Rossum - - * ChangeLog, ColorDelegator.py, PyShell.py: Tim Peters strikes again: - - Ho ho ho -- that's trickier than it sounded! The colorizer is working with - "line.col" strings instead of Text marks, and the absolute coordinates of - the point of interest can change across the self.update call (voice of - baffled experience, when two quick backspaces no longer fooled it, but a - backspace followed by a quick ENTER did ). - - Anyway, the attached appears to do the trick. CPU usage goes way up when - typing quickly into a long triple-quoted string, but the latency is fine for - me (a relatively fast typist on a relatively slow machine). Most of the - changes here are left over from reducing the # of vrbl names to help me - reason about the logic better; I hope the code is a *little* easier to - -Fri Apr 23 14:01:25 1999 Guido van Rossum - - * EditorWindow.py: - Provide full arguments to __import__ so it works in packagized IDLE. - -Thu Apr 22 23:20:17 1999 Guido van Rossum - - * help.txt: - Bunch of updates necessary due to recent changes; added docs for File - menu, command line and color preferences. - - * Bindings.py: Remove obsolete 'script' menu. - - * TODO.txt: Several wishes fulfilled. - - * OutputWindow.py: - Moved classes OnDemandOutputWindow and PseudoFile here, - from ScriptBinding.py where they are no longer needed. - - * ScriptBinding.py: - Mostly rewritten. Instead of the old Run module and Debug module, - there are two new commands: - - Import module (F5) imports or reloads the module and also adds its - name to the __main__ namespace. This gets executed in the PyShell - window under control of its debug settings. - - Run script (Control-F5) is similar but executes the contents of the - file directly in the __main__ namespace. - - * PyShell.py: Nits: document use of $IDLESTARTUP; display idle version - - * idlever.py: New version to celebrate new command line - - * OutputWindow.py: Added flush(), for completeness. - - * PyShell.py: - A lot of changes to make the command line more useful. You can now do: - idle.py -e file ... -- to edit files - idle.py script arg ... -- to run a script - idle.py -c cmd arg ... -- to run a command - Other options, see also the usage message (also new!) for more details: - -d -- enable debugger - -s -- run $IDLESTARTUP or $PYTHONSTARTUP - -t title -- set Python Shell window's title - sys.argv is set accordingly, unless -e is used. - sys.path is absolutized, and all relevant paths are inserted into it. - - Other changes: - - the environment in which commands are executed is now the - __main__ module - - explicitly save sys.stdout etc., don't restore from sys.__stdout__ - - new interpreter methods execsource(), execfile(), stuffsource() - - a few small nits - - * TODO.txt: - Some more TODO items. Made up my mind about command line args, - Run/Import, __main__. - - * ColorDelegator.py: - Super-elegant patch by Tim Peters that speeds up colorization - dramatically (up to 15 times he claims). Works by reading more than - one line at a time, up to 100-line chunks (starting with one line and - then doubling up to the limit). On a typical machine (e.g. Tim's - P5-166) this doesn't reduce interactive responsiveness in a noticeable - way. - -Wed Apr 21 15:49:34 1999 Guido van Rossum - - * ColorDelegator.py: - Patch by Tim Peters to speed up colorizing of big multiline strings. - -Tue Apr 20 17:32:52 1999 Guido van Rossum - - * extend.txt: - For an event 'foo-bar', the corresponding method must be called - foo_bar_event(). Therefore, fix the references to zoom_height() in - the example. - - * IdlePrefs.py: Restored the original IDLE color scheme. - - * PyShell.py, IdlePrefs.py, ColorDelegator.py, EditorWindow.py: - Color preferences code by Loren Luke (massaged by me somewhat) - - * SearchEngine.py: - Patch by Mark Favas: it fixes the search engine behaviour where an - unsuccessful search wraps around and re-searches that part of the file - between the start of the search and the end of the file - only really - an issue for very large files, but... (also removes a redundant - m.span() call). - -Mon Apr 19 16:26:02 1999 Guido van Rossum - - * TODO.txt: A few wishes are now fulfilled. - - * AutoIndent.py: Tim Peters implements some of my wishes: - - o Makes the tab key intelligently insert spaces when appropriate - (see Help list banter twixt David Ascher and me; idea stolen from - every other editor on earth ). - - o newline_and_indent_event trims trailing whitespace on the old - line (pymode and Codewright). - - o newline_and_indent_event no longer fooled by trailing whitespace or - comment after ":" (pymode, PTUI). - - o newline_and_indent_event now reduces the new line's indentation after - return, break, continue, raise and pass stmts (pymode). - - The last two are easy to fool in the presence of strings & - continuations, but pymode requires Emacs's high-powered C parsing - functions to avoid that in finite time. - -====================================================================== - Python release 1.5.2c1, IDLE version 0.4 -====================================================================== - -Wed Apr 7 18:41:59 1999 Guido van Rossum - - * README.txt, NEWS.txt: New version. - - * idlever.py: Version bump awaiting impending new release. - (Not much has changed :-( ) - -Mon Mar 29 14:52:28 1999 Guido van Rossum - - * ScriptBinding.py, PyShell.py: - At Tim Peters' recommendation, add a dummy flush() method to - PseudoFile. - -Thu Mar 11 23:21:23 1999 Guido van Rossum - - * PathBrowser.py: Don't crash when sys.path contains an empty string. - - * Attic/Outline.py: This file was never supposed to be part of IDLE. - - * PathBrowser.py: - - Don't crash in the case where a superclass is a string instead of a - pyclbr.Class object; this can happen when the superclass is - unrecognizable (to pyclbr), e.g. when module renaming is used. - - - Show a watch cursor when calling pyclbr (since it may take a while - recursively parsing imported modules!). - -Wed Mar 10 05:18:02 1999 Guido van Rossum - - * EditorWindow.py, Bindings.py: Add PathBrowser to File module - - * PathBrowser.py: "Path browser" - 4 scrolled lists displaying: - directories on sys.path - modules in selected directory - classes in selected module - methods of selected class - - Sinlge clicking in a directory, module or class item updates the next - column with info about the selected item. Double clicking in a - module, class or method item opens the file (and selects the clicked - item if it is a class or method). - - I guess eventually I should be using a tree widget for this, but the - ones I've seen don't work well enough, so for now I use the old - Smalltalk or NeXT style multi-column hierarchical browser. - - * MultiScrolledLists.py: - New utility: multiple scrolled lists in parallel - - * ScrolledList.py: - White background. - - Display "(None)" (or text of your choosing) when empty. - - Don't set the focus. - -====================================================================== - Python release 1.5.2b2, IDLE version 0.3 -====================================================================== - -Wed Feb 17 22:47:41 1999 Guido van Rossum - - * NEWS.txt: News in 0.3. - - * README.txt, idlever.py: Bump version to 0.3. - - * EditorWindow.py: - After all, we don't need to call the callbacks ourselves! - - * WindowList.py: - When deleting, call the callbacks *after* deleting the window from our list! - - * EditorWindow.py: - Fix up the Windows menu via the new callback mechanism instead of - depending on menu post commands (which don't work when the menu is - torn off). - - * WindowList.py: - Support callbacks to patch up Windows menus everywhere. - - * ChangeLog: Oh, why not. Checking in the Emacs-generated change log. - -Tue Feb 16 22:34:17 1999 Guido van Rossum - - * ScriptBinding.py: - Only pop up the stack viewer when requested in the Debug menu. - -Mon Feb 8 22:27:49 1999 Guido van Rossum - - * WindowList.py: Don't crash if a window no longer exists. - - * TODO.txt: Restructured a bit. - -Mon Feb 1 23:06:17 1999 Guido van Rossum - - * PyShell.py: Add current dir or paths of file args to sys.path. - - * Debugger.py: Add canonic() function -- for brand new bdb.py feature. - - * StackViewer.py: Protect against accessing an empty stack. - -Fri Jan 29 20:44:45 1999 Guido van Rossum - - * ZoomHeight.py: - Use only the height to decide whether to zoom in or out. - -Thu Jan 28 22:24:30 1999 Guido van Rossum - - * EditorWindow.py, FileList.py: - Make sure the Tcl variables are shared between windows. - - * PyShell.py, EditorWindow.py, Bindings.py: - Move menu/key binding code from Bindings.py to EditorWindow.py, - with changed APIs -- it makes much more sense there. - Also add a new feature: if the first character of a menu label is - a '!', it gets a checkbox. Checkboxes are bound to Boolean Tcl variables - that can be accessed through the new getvar/setvar/getrawvar API; - the variable is named after the event to which the menu is bound. - - * Debugger.py: Add Quit button to the debugger window. - - * SearchDialog.py: - When find_again() finds exactly the current selection, it's a failure. - - * idle.py, Attic/idle: Rename idle -> idle.py - -Mon Jan 18 15:18:57 1999 Guido van Rossum - - * EditorWindow.py, WindowList.py: Only deiconify when iconic. - - * TODO.txt: Misc - -Tue Jan 12 22:14:34 1999 Guido van Rossum - - * testcode.py, Attic/test.py: - Renamed test.py to testcode.py so one can import Python's - test package from inside IDLE. (Suggested by Jack Jansen.) - - * EditorWindow.py, ColorDelegator.py: - Hack to close a window that is colorizing. - - * Separator.py: Vladimir Marangozov's patch: - The separator dances too much and seems to jump by arbitrary amounts - in arbitrary directions when I try to move it for resizing the frames. - This patch makes it more quiet. - -Mon Jan 11 14:52:40 1999 Guido van Rossum - - * TODO.txt: Some requests have been fulfilled. - - * EditorWindow.py: - Set the cursor to a watch when opening the class browser (which may - take quite a while, browsing multiple files). - - Newer, better center() -- but assumes no wrapping. - - * SearchBinding.py: - Got rid of debug print statement in goto_line_event(). - - * ScriptBinding.py: - I think I like it better if it prints the traceback even when it displays - the stack viewer. - - * Debugger.py: Bind ESC to close-window. - - * ClassBrowser.py: Use a HSeparator between the classes and the items. - Make the list of classes wider by default (40 chars). - Bind ESC to close-window. - - * Separator.py: - Separator classes (draggable divider between two panes). - -Sat Jan 9 22:01:33 1999 Guido van Rossum - - * WindowList.py: - Don't traceback when wakeup() is called when the window has been destroyed. - This can happen when a torn-of Windows menu references closed windows. - And Tim Peters claims that the Windows menu is his favorite to tear off... - - * EditorWindow.py: Allow tearing off of the Windows menu. - - * StackViewer.py: Close on ESC. - - * help.txt: Updated a bunch of things (it was mostly still 0.1!) - - * extend.py: Added ScriptBinding to standard bindings. - - * ScriptBinding.py: - This now actually works. See doc string. It can run a module (i.e. - import or reload) or debug it (same with debugger control). Output - goes to a fresh output window, only created when needed. - -====================================================================== - Python release 1.5.2b1, IDLE version 0.2 -====================================================================== - -Fri Jan 8 17:26:02 1999 Guido van Rossum - - * README.txt, NEWS.txt: What's new in this release. - - * Bindings.py, PyShell.py: - Paul Prescod's patches to allow the stack viewer to pop up when a - traceback is printed. - -Thu Jan 7 00:12:15 1999 Guido van Rossum - - * FormatParagraph.py: - Change paragraph width limit to 70 (like Emacs M-Q). - - * README.txt: - Separating TODO from README. Slight reformulation of features. No - exact release date. - - * TODO.txt: Separating TODO from README. - -Mon Jan 4 21:19:09 1999 Guido van Rossum - - * FormatParagraph.py: - Hm. There was a boundary condition error at the end of the file too. - - * SearchBinding.py: Hm. Add Unix binding for replace, too. - - * keydefs.py: Ran eventparse.py again. - - * FormatParagraph.py: Added Unix Meta-q key binding; - fix find_paragraph when at start of file. - - * AutoExpand.py: Added Meta-/ binding for Unix as alt for Alt-/. - - * SearchBinding.py: - Add unix binding for grep (otherwise the menu entry doesn't work!) - - * ZoomHeight.py: Adjusted Unix height to work with fvwm96. :=( - - * GrepDialog.py: Need to import sys! - - * help.txt, extend.txt, README.txt: Formatted some paragraphs - - * extend.py, FormatParagraph.py: - Add new extension to reformat a (text) paragraph. - - * ZoomHeight.py: Typo in Win specific height setting. - -Sun Jan 3 00:47:35 1999 Guido van Rossum - - * AutoIndent.py: Added something like Tim Peters' backspace patch. - - * ZoomHeight.py: Adapted to Unix (i.e., more hardcoded constants). - -Sat Jan 2 21:28:54 1999 Guido van Rossum - - * keydefs.py, idlever.py, idle.pyw, idle.bat, help.txt, extend.txt, extend.py, eventparse.py, ZoomHeight.py, WindowList.py, UndoDelegator.py, StackViewer.py, SearchEngine.py, SearchDialogBase.py, SearchDialog.py, ScrolledList.py, SearchBinding.py, ScriptBinding.py, ReplaceDialog.py, Attic/README, README.txt, PyShell.py, Attic/PopupMenu.py, OutputWindow.py, IOBinding.py, Attic/HelpWindow.py, History.py, GrepDialog.py, FileList.py, FrameViewer.py, EditorWindow.py, Debugger.py, Delegator.py, ColorDelegator.py, Bindings.py, ClassBrowser.py, AutoExpand.py, AutoIndent.py: - Checking in IDLE 0.2. - - Much has changed -- too much, in fact, to write down. - The big news is that there's a standard way to write IDLE extensions; - see extend.txt. Some sample extensions have been provided, and - some existing code has been converted to extensions. Probably the - biggest new user feature is a new search dialog with more options, - search and replace, and even search in files (grep). - - This is exactly as downloaded from my laptop after returning - from the holidays -- it hasn't even been tested on Unix yet. - -Fri Dec 18 15:52:54 1998 Guido van Rossum - - * FileList.py, ClassBrowser.py: - Fix the class browser to work even when the file is not on sys.path. - -Tue Dec 8 20:39:36 1998 Guido van Rossum - - * Attic/turtle.py: Moved to Python 1.5.2/Lib - -Fri Nov 27 03:19:20 1998 Guido van Rossum - - * help.txt: Typo - - * EditorWindow.py, FileList.py: Support underlining of menu labels - - * Bindings.py: - New approach, separate tables for menus (platform-independent) and key - definitions (platform-specific), and generating accelerator strings - automatically from the key definitions. - -Mon Nov 16 18:37:42 1998 Guido van Rossum - - * Attic/README: Clarify portability and main program. - - * Attic/README: Added intro for 0.1 release and append Grail notes. - -Mon Oct 26 18:49:00 1998 Guido van Rossum - - * Attic/turtle.py: root is now a global called _root - -Sat Oct 24 16:38:38 1998 Guido van Rossum - - * Attic/turtle.py: Raise the root window on reset(). - Different action on WM_DELETE_WINDOW is more likely to do the right thing, - allowing us to destroy old windows. - - * Attic/turtle.py: - Split the goto() function in two: _goto() is the internal one, - using Canvas coordinates, and goto() uses turtle coordinates - and accepts variable argument lists. - - * Attic/turtle.py: Cope with destruction of the window - - * Attic/turtle.py: Turtle graphics - - * Debugger.py: Use of Breakpoint class should be bdb.Breakpoint. - -Mon Oct 19 03:33:40 1998 Guido van Rossum - - * SearchBinding.py: - Speed up the search a bit -- don't drag a mark around... - - * PyShell.py: - Change our special entries from to . - Patch linecache.checkcache() to keep our special entries alive. - Add popup menu to all editor windows to set a breakpoint. - - * Debugger.py: - Use and pass through the 'force' flag to set_dict() where appropriate. - Default source and globals checkboxes to false. - Don't interact in user_return(). - Add primitive set_breakpoint() method. - - * ColorDelegator.py: - Raise priority of 'sel' tag so its foreground (on Windows) will take - priority over text colorization (which on Windows is almost the - same color as the selection background). - - Define a tag and color for breakpoints ("BREAK"). - - * Attic/PopupMenu.py: Disable "Open stack viewer" and "help" commands. - - * StackViewer.py: - Add optional 'force' argument (default 0) to load_dict(). - If set, redo the display even if it's the same dict. - -Fri Oct 16 21:10:12 1998 Guido van Rossum - - * StackViewer.py: Do nothing when loading the same dict as before. - - * PyShell.py: Details for debugger interface. - - * Debugger.py: - Restructured and more consistent. Save checkboxes across instantiations. - - * EditorWindow.py, Attic/README, Bindings.py: - Get rid of conflicting ^X binding. Use ^W. - - * Debugger.py, StackViewer.py: - Debugger can now show local and global variables. - - * Debugger.py: Oops - - * Debugger.py, PyShell.py: Better debugger support (show stack etc). - - * Attic/PopupMenu.py: Follow renames in StackViewer module - - * StackViewer.py: - Rename classes to StackViewer (the widget) and StackBrowser (the toplevel). - - * ScrolledList.py: Add close() method - - * EditorWindow.py: Clarify 'Open Module' dialog text - - * StackViewer.py: Restructured into a browser and a widget. - -Thu Oct 15 23:27:08 1998 Guido van Rossum - - * ClassBrowser.py, ScrolledList.py: - Generalized the scrolled list which is the base for the class and - method browser into a separate class in its own module. - - * Attic/test.py: Cosmetic change - - * Debugger.py: Don't show function name if there is none - -Wed Oct 14 03:43:05 1998 Guido van Rossum - - * Debugger.py, PyShell.py: Polish the Debugger GUI a bit. - Closing it now also does the right thing. - -Tue Oct 13 23:51:13 1998 Guido van Rossum - - * Debugger.py, PyShell.py, Bindings.py: - Ad primitive debugger interface (so far it will step and show you the - source, but it doesn't yet show the stack). - - * Attic/README: Misc - - * StackViewer.py: Whoops -- referenced self.top before it was set. - - * help.txt: Added history and completion commands. - - * help.txt: Updated - - * FileList.py: Add class browser functionality. - - * StackViewer.py: - Add a close() method and bind to WM_DELETE_WINDOW protocol - - * PyShell.py: Clear the linecache before printing a traceback - - * Bindings.py: Added class browser binding. - - * ClassBrowser.py: Much improved, much left to do. - - * PyShell.py: Make the return key do what I mean more often. - - * ClassBrowser.py: - Adding the beginnings of a Class browser. Incomplete, yet. - - * EditorWindow.py, Bindings.py: - Add new command, "Open module". You select or type a module name, - and it opens the source. - -Mon Oct 12 23:59:27 1998 Guido van Rossum - - * PyShell.py: Subsume functionality from Popup menu in Debug menu. - Other stuff so the PyShell window can be resurrected from the Windows menu. - - * FileList.py: Get rid of PopUp menu. - Create a simple Windows menu. (Imperfect when Untitled windows exist.) - Add wakeup() method: deiconify, raise, focus. - - * EditorWindow.py: Generalize menu creation. - - * Bindings.py: Add Debug and Help menu items. - - * EditorWindow.py: Added a menu bar to every window. - - * Bindings.py: Add menu configuration to the event configuration. - - * Attic/PopupMenu.py: Pass a root to the help window. - - * SearchBinding.py: - Add parent argument to 'to to line number' dialog box. - -Sat Oct 10 19:15:32 1998 Guido van Rossum - - * StackViewer.py: - Add a label at the top showing (very basic) help for the stack viewer. - Add a label at the bottom showing the exception info. - - * Attic/test.py, Attic/idle: Add Unix main script and test program. - - * idle.pyw, help.txt, WidgetRedirector.py, UndoDelegator.py, StackViewer.py, SearchBinding.py, Attic/README, PyShell.py, Attic/PopupMenu.py, Percolator.py, Outline.py, IOBinding.py, History.py, Attic/HelpWindow.py, FrameViewer.py, FileList.py, EditorWindow.py, Delegator.py, ColorDelegator.py, Bindings.py, AutoIndent.py, AutoExpand.py: - Initial checking of Tk-based Python IDE. - Features: text editor with syntax coloring and undo; - subclassed into interactive Python shell which adds history. - diff --git a/Lib/idlelib/ClassBrowser.py b/Lib/idlelib/ClassBrowser.py deleted file mode 100644 index 19f3b7e6003c..000000000000 --- a/Lib/idlelib/ClassBrowser.py +++ /dev/null @@ -1,224 +0,0 @@ -"""Class browser. - -XXX TO DO: - -- reparse when source changed (maybe just a button would be OK?) - (or recheck on window popup) -- add popup menu with more options (e.g. doc strings, base classes, imports) -- show function argument list? (have to do pattern matching on source) -- should the classes and methods lists also be in the module's menu bar? -- add base classes to class browser tree -""" - -import os -import sys -import string -import pyclbr - -# XXX Patch pyclbr with dummies if it's vintage Python 1.5.2: -if not hasattr(pyclbr, "readmodule_ex"): - pyclbr.readmodule_ex = pyclbr.readmodule -if not hasattr(pyclbr, "Function"): - class Function(pyclbr.Class): - pass - pyclbr.Function = Function - -import PyShell -from WindowList import ListedToplevel -from TreeWidget import TreeNode, TreeItem, ScrolledCanvas - -class ClassBrowser: - - def __init__(self, flist, name, path): - # XXX This API should change, if the file doesn't end in ".py" - # XXX the code here is bogus! - self.name = name - self.file = os.path.join(path[0], self.name + ".py") - self.init(flist) - - def close(self, event=None): - self.top.destroy() - self.node.destroy() - - def init(self, flist): - self.flist = flist - # reset pyclbr - pyclbr._modules.clear() - # create top - self.top = top = ListedToplevel(flist.root) - top.protocol("WM_DELETE_WINDOW", self.close) - top.bind("", self.close) - self.settitle() - top.focus_set() - # create scrolled canvas - sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1) - sc.frame.pack(expand=1, fill="both") - item = self.rootnode() - self.node = node = TreeNode(sc.canvas, None, item) - node.update() - node.expand() - - def settitle(self): - self.top.wm_title("Class Browser - " + self.name) - self.top.wm_iconname("Class Browser") - - def rootnode(self): - return ModuleBrowserTreeItem(self.file) - -class ModuleBrowserTreeItem(TreeItem): - - def __init__(self, file): - self.file = file - - def GetText(self): - return os.path.basename(self.file) - - def GetIconName(self): - return "python" - - def GetSubList(self): - sublist = [] - for name in self.listclasses(): - item = ClassBrowserTreeItem(name, self.classes, self.file) - sublist.append(item) - return sublist - - def OnDoubleClick(self): - if os.path.normcase(self.file[-3:]) != ".py": - return - if not os.path.exists(self.file): - return - PyShell.flist.open(self.file) - - def IsExpandable(self): - return os.path.normcase(self.file[-3:]) == ".py" - - def listclasses(self): - dir, file = os.path.split(self.file) - name, ext = os.path.splitext(file) - if os.path.normcase(ext) != ".py": - return [] - try: - dict = pyclbr.readmodule_ex(name, [dir] + sys.path) - except ImportError, msg: - return [] - items = [] - self.classes = {} - for key, cl in dict.items(): - if cl.module == name: - s = key - if cl.super: - supers = [] - for sup in cl.super: - if type(sup) is type(''): - sname = sup - else: - sname = sup.name - if sup.module != cl.module: - sname = "%s.%s" % (sup.module, sname) - supers.append(sname) - s = s + "(%s)" % string.join(supers, ", ") - items.append((cl.lineno, s)) - self.classes[s] = cl - items.sort() - list = [] - for item, s in items: - list.append(s) - return list - -class ClassBrowserTreeItem(TreeItem): - - def __init__(self, name, classes, file): - self.name = name - self.classes = classes - self.file = file - try: - self.cl = self.classes[self.name] - except (IndexError, KeyError): - self.cl = None - self.isfunction = isinstance(self.cl, pyclbr.Function) - - def GetText(self): - if self.isfunction: - return "def " + self.name + "(...)" - else: - return "class " + self.name - - def GetIconName(self): - if self.isfunction: - return "python" - else: - return "folder" - - def IsExpandable(self): - if self.cl: - return not not self.cl.methods - - def GetSubList(self): - if not self.cl: - return [] - sublist = [] - for name in self.listmethods(): - item = MethodBrowserTreeItem(name, self.cl, self.file) - sublist.append(item) - return sublist - - def OnDoubleClick(self): - if not os.path.exists(self.file): - return - edit = PyShell.flist.open(self.file) - if hasattr(self.cl, 'lineno'): - lineno = self.cl.lineno - edit.gotoline(lineno) - - def listmethods(self): - if not self.cl: - return [] - items = [] - for name, lineno in self.cl.methods.items(): - items.append((lineno, name)) - items.sort() - list = [] - for item, name in items: - list.append(name) - return list - -class MethodBrowserTreeItem(TreeItem): - - def __init__(self, name, cl, file): - self.name = name - self.cl = cl - self.file = file - - def GetText(self): - return "def " + self.name + "(...)" - - def GetIconName(self): - return "python" # XXX - - def IsExpandable(self): - return 0 - - def OnDoubleClick(self): - if not os.path.exists(self.file): - return - edit = PyShell.flist.open(self.file) - edit.gotoline(self.cl.methods[self.name]) - -def main(): - try: - file = __file__ - except NameError: - file = sys.argv[0] - if sys.argv[1:]: - file = sys.argv[1] - else: - file = sys.argv[0] - dir, file = os.path.split(file) - name = os.path.splitext(file)[0] - ClassBrowser(PyShell.flist, name, [dir]) - if sys.stdin is sys.__stdin__: - mainloop() - -if __name__ == "__main__": - main() diff --git a/Lib/idlelib/ColorDelegator.py b/Lib/idlelib/ColorDelegator.py deleted file mode 100644 index b4d655950eac..000000000000 --- a/Lib/idlelib/ColorDelegator.py +++ /dev/null @@ -1,249 +0,0 @@ -import time -import string -import re -import keyword -from Tkinter import * -from Delegator import Delegator -from configHandler import idleConf - -#$ event <> -#$ win -#$ unix - -DEBUG = 0 - - -def any(name, list): - return "(?P<%s>" % name + string.join(list, "|") + ")" - -def make_pat(): - kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" - comment = any("COMMENT", [r"#[^\n]*"]) - sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" - dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' - sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" - dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' - string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) - return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"]) - -prog = re.compile(make_pat(), re.S) -idprog = re.compile(r"\s+(\w+)", re.S) -asprog = re.compile(r".*?\b(as)\b", re.S) - -class ColorDelegator(Delegator): - - def __init__(self): - Delegator.__init__(self) - self.prog = prog - self.idprog = idprog - self.asprog = asprog - - def setdelegate(self, delegate): - if self.delegate is not None: - self.unbind("<>") - Delegator.setdelegate(self, delegate) - if delegate is not None: - self.config_colors() - self.bind("<>", self.toggle_colorize_event) - self.notify_range("1.0", "end") - - def config_colors(self): - for tag, cnf in self.tagdefs.items(): - if cnf: - apply(self.tag_configure, (tag,), cnf) - self.tag_raise('sel') - - theme = idleConf.GetOption('main','Theme','name') - - tagdefs = { - "COMMENT": idleConf.GetHighlight(theme, "comment"), - "KEYWORD": idleConf.GetHighlight(theme, "keyword"), - "STRING": idleConf.GetHighlight(theme, "string"), - "DEFINITION": idleConf.GetHighlight(theme, "definition"), - "SYNC": idleConf.GetHighlight(theme, "sync"), - "TODO": idleConf.GetHighlight(theme, "todo"), - "BREAK": idleConf.GetHighlight(theme, "break"), - # The following is used by ReplaceDialog: - "hit": idleConf.GetHighlight(theme, "hit"), - } - - if DEBUG: print 'tagdefs',tagdefs - - def insert(self, index, chars, tags=None): - index = self.index(index) - self.delegate.insert(index, chars, tags) - self.notify_range(index, index + "+%dc" % len(chars)) - - def delete(self, index1, index2=None): - index1 = self.index(index1) - self.delegate.delete(index1, index2) - self.notify_range(index1) - - after_id = None - allow_colorizing = 1 - colorizing = 0 - - def notify_range(self, index1, index2=None): - self.tag_add("TODO", index1, index2) - if self.after_id: - if DEBUG: print "colorizing already scheduled" - return - if self.colorizing: - self.stop_colorizing = 1 - if DEBUG: print "stop colorizing" - if self.allow_colorizing: - if DEBUG: print "schedule colorizing" - self.after_id = self.after(1, self.recolorize) - - close_when_done = None # Window to be closed when done colorizing - - def close(self, close_when_done=None): - if self.after_id: - after_id = self.after_id - self.after_id = None - if DEBUG: print "cancel scheduled recolorizer" - self.after_cancel(after_id) - self.allow_colorizing = 0 - self.stop_colorizing = 1 - if close_when_done: - if not self.colorizing: - close_when_done.destroy() - else: - self.close_when_done = close_when_done - - def toggle_colorize_event(self, event): - if self.after_id: - after_id = self.after_id - self.after_id = None - if DEBUG: print "cancel scheduled recolorizer" - self.after_cancel(after_id) - if self.allow_colorizing and self.colorizing: - if DEBUG: print "stop colorizing" - self.stop_colorizing = 1 - self.allow_colorizing = not self.allow_colorizing - if self.allow_colorizing and not self.colorizing: - self.after_id = self.after(1, self.recolorize) - if DEBUG: - print "auto colorizing turned", self.allow_colorizing and "on" or "off" - return "break" - - def recolorize(self): - self.after_id = None - if not self.delegate: - if DEBUG: print "no delegate" - return - if not self.allow_colorizing: - if DEBUG: print "auto colorizing is off" - return - if self.colorizing: - if DEBUG: print "already colorizing" - return - try: - self.stop_colorizing = 0 - self.colorizing = 1 - if DEBUG: print "colorizing..." - t0 = time.clock() - self.recolorize_main() - t1 = time.clock() - if DEBUG: print "%.3f seconds" % (t1-t0) - finally: - self.colorizing = 0 - if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): - if DEBUG: print "reschedule colorizing" - self.after_id = self.after(1, self.recolorize) - if self.close_when_done: - top = self.close_when_done - self.close_when_done = None - top.destroy() - - def recolorize_main(self): - next = "1.0" - while 1: - item = self.tag_nextrange("TODO", next) - if not item: - break - head, tail = item - self.tag_remove("SYNC", head, tail) - item = self.tag_prevrange("SYNC", head) - if item: - head = item[1] - else: - head = "1.0" - - chars = "" - next = head - lines_to_get = 1 - ok = 0 - while not ok: - mark = next - 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) - ##print head, "get", mark, next, "->", `line` - if not line: - return - for tag in self.tagdefs.keys(): - self.tag_remove(tag, mark, next) - chars = chars + line - m = self.prog.search(chars) - while m: - for key, value in m.groupdict().items(): - if value: - a, b = m.span(key) - self.tag_add(key, - head + "+%dc" % a, - head + "+%dc" % b) - if value in ("def", "class"): - m1 = self.idprog.match(chars, b) - if m1: - a, b = m1.span(1) - self.tag_add("DEFINITION", - head + "+%dc" % a, - head + "+%dc" % b) - elif value == "import": - # color all the "as" words on same line; - # cheap approximation to the truth - while 1: - m1 = self.asprog.match(chars, b) - if not m1: - break - a, b = m1.span(1) - self.tag_add("KEYWORD", - head + "+%dc" % a, - head + "+%dc" % b) - m = self.prog.search(chars, m.end()) - if "SYNC" in self.tag_names(next + "-1c"): - head = next - chars = "" - else: - ok = 0 - if not ok: - # We're in an inconsistent state, and the call to - # update may tell us to stop. It may also change - # the correct value for "next" (since this is a - # line.col string, not a true mark). So leave a - # crumb telling the next invocation to resume here - # in case update tells us to leave. - self.tag_add("TODO", next) - self.update() - if self.stop_colorizing: - if DEBUG: print "colorizing stopped" - return - - -def main(): - from Percolator import Percolator - root = Tk() - root.wm_protocol("WM_DELETE_WINDOW", root.quit) - text = Text(background="white") - text.pack(expand=1, fill="both") - text.focus_set() - p = Percolator(text) - d = ColorDelegator() - p.insertfilter(d) - root.mainloop() - -if __name__ == "__main__": - main() diff --git a/Lib/idlelib/Debugger.py b/Lib/idlelib/Debugger.py deleted file mode 100644 index e4591ff6df16..000000000000 --- a/Lib/idlelib/Debugger.py +++ /dev/null @@ -1,308 +0,0 @@ -import os -import bdb -import traceback -from Tkinter import * -from WindowList import ListedToplevel - -import StackViewer - - -class Debugger(bdb.Bdb): - - interacting = 0 - - vstack = vsource = vlocals = vglobals = None - - def __init__(self, pyshell): - bdb.Bdb.__init__(self) - self.pyshell = pyshell - self.make_gui() - - def canonic(self, filename): - # Canonicalize filename -- called by Bdb - return os.path.normcase(os.path.abspath(filename)) - - def close(self, event=None): - if self.interacting: - self.top.bell() - return - if self.stackviewer: - self.stackviewer.close(); self.stackviewer = None - self.pyshell.close_debugger() - self.top.destroy() - - def run(self, *args): - try: - self.interacting = 1 - return apply(bdb.Bdb.run, (self,) + args) - finally: - self.interacting = 0 - - def user_line(self, frame): - self.interaction(frame) - - def user_return(self, frame, rv): - # XXX show rv? - ##self.interaction(frame) - pass - - def user_exception(self, frame, info): - self.interaction(frame, info) - - def make_gui(self): - pyshell = self.pyshell - self.flist = pyshell.flist - self.root = root = pyshell.root - self.top = top =ListedToplevel(root) - self.top.wm_title("Debug Control") - self.top.wm_iconname("Debug") - top.wm_protocol("WM_DELETE_WINDOW", self.close) - self.top.bind("", self.close) - # - self.bframe = bframe = Frame(top) - self.bframe.pack(anchor="w") - self.buttons = bl = [] - # - self.bcont = b = Button(bframe, text="Go", command=self.cont) - bl.append(b) - self.bstep = b = Button(bframe, text="Step", command=self.step) - bl.append(b) - self.bnext = b = Button(bframe, text="Over", command=self.next) - bl.append(b) - self.bret = b = Button(bframe, text="Out", command=self.ret) - bl.append(b) - self.bret = b = Button(bframe, text="Quit", command=self.quit) - bl.append(b) - # - for b in bl: - b.configure(state="disabled") - b.pack(side="left") - # - self.cframe = cframe = Frame(bframe) - self.cframe.pack(side="left") - # - if not self.vstack: - self.__class__.vstack = BooleanVar(top) - self.vstack.set(1) - self.bstack = Checkbutton(cframe, - text="Stack", command=self.show_stack, variable=self.vstack) - self.bstack.grid(row=0, column=0) - if not self.vsource: - self.__class__.vsource = BooleanVar(top) - ##self.vsource.set(1) - self.bsource = Checkbutton(cframe, - text="Source", command=self.show_source, variable=self.vsource) - self.bsource.grid(row=0, column=1) - if not self.vlocals: - self.__class__.vlocals = BooleanVar(top) - self.vlocals.set(1) - self.blocals = Checkbutton(cframe, - text="Locals", command=self.show_locals, variable=self.vlocals) - self.blocals.grid(row=1, column=0) - if not self.vglobals: - self.__class__.vglobals = BooleanVar(top) - ##self.vglobals.set(1) - self.bglobals = Checkbutton(cframe, - 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.errorbg = self.error.cget("background") - # - self.fstack = Frame(top, height=1) - self.fstack.pack(expand=1, fill="both") - self.flocals = Frame(top) - self.flocals.pack(expand=1, fill="both") - self.fglobals = Frame(top, height=1) - self.fglobals.pack(expand=1, fill="both") - # - if self.vstack.get(): - self.show_stack() - if self.vlocals.get(): - self.show_locals() - if self.vglobals.get(): - self.show_globals() - - frame = None - - def interaction(self, frame, info=None): - self.frame = frame - code = frame.f_code - file = code.co_filename - base = os.path.basename(file) - lineno = frame.f_lineno - # - message = "%s:%s" % (base, lineno) - if code.co_name != "?": - message = "%s: %s()" % (message, code.co_name) - self.status.configure(text=message) - # - if info: - type, value, tb = info - try: - m1 = type.__name__ - except AttributeError: - m1 = "%s" % str(type) - if value is not None: - try: - m1 = "%s: %s" % (m1, str(value)) - except: - pass - bg = "yellow" - else: - m1 = "" - tb = None - bg = self.errorbg - self.error.configure(text=m1, background=bg) - # - sv = self.stackviewer - if sv: - stack, i = self.get_stack(self.frame, tb) - sv.load_stack(stack, i) - # - self.show_variables(1) - # - if self.vsource.get(): - self.sync_source_line() - # - for b in self.buttons: - b.configure(state="normal") - # - self.top.tkraise() - self.root.mainloop() - # - for b in self.buttons: - b.configure(state="disabled") - self.status.configure(text="") - self.error.configure(text="", background=self.errorbg) - self.frame = None - - def sync_source_line(self): - frame = self.frame - if not frame: - return - code = frame.f_code - file = code.co_filename - lineno = frame.f_lineno - if file[:1] + file[-1:] != "<>" and os.path.exists(file): - edit = self.flist.open(file) - if edit: - edit.gotoline(lineno) - - def cont(self): - self.set_continue() - self.root.quit() - - def step(self): - self.set_step() - self.root.quit() - - def next(self): - self.set_next(self.frame) - self.root.quit() - - def ret(self): - self.set_return(self.frame) - self.root.quit() - - def quit(self): - self.set_quit() - self.root.quit() - - stackviewer = None - - def show_stack(self): - if not self.stackviewer and self.vstack.get(): - self.stackviewer = sv = StackViewer.StackViewer( - self.fstack, self.flist, self) - if self.frame: - stack, i = self.get_stack(self.frame, None) - sv.load_stack(stack, i) - else: - sv = self.stackviewer - if sv and not self.vstack.get(): - self.stackviewer = None - sv.close() - self.fstack['height'] = 1 - - def show_source(self): - if self.vsource.get(): - self.sync_source_line() - - def show_frame(self, (frame, lineno)): - self.frame = frame - self.show_variables() - - localsviewer = None - globalsviewer = None - - def show_locals(self): - lv = self.localsviewer - if self.vlocals.get(): - if not lv: - self.localsviewer = StackViewer.NamespaceViewer( - self.flocals, "Locals") - else: - if lv: - self.localsviewer = None - lv.close() - self.flocals['height'] = 1 - self.show_variables() - - def show_globals(self): - gv = self.globalsviewer - if self.vglobals.get(): - if not gv: - self.globalsviewer = StackViewer.NamespaceViewer( - self.fglobals, "Globals") - else: - if gv: - self.globalsviewer = None - gv.close() - self.fglobals['height'] = 1 - self.show_variables() - - def show_variables(self, force=0): - lv = self.localsviewer - gv = self.globalsviewer - frame = self.frame - if not frame: - ldict = gdict = None - else: - ldict = frame.f_locals - gdict = frame.f_globals - if lv and gv and ldict is gdict: - ldict = None - if lv: - lv.load_dict(ldict, force) - if gv: - gv.load_dict(gdict, force) - - def set_breakpoint_here(self, edit): - text = edit.text - filename = edit.io.filename - if not filename: - text.bell() - return - lineno = int(float(text.index("insert"))) - msg = self.set_break(filename, lineno) - if msg: - text.bell() - return - text.tag_add("BREAK", "insert linestart", "insert lineend +1char") - - # A literal copy of Bdb.set_break() without the print statement at the end - def set_break(self, filename, lineno, temporary=0, cond = None): - import linecache # Import as late as possible - line = linecache.getline(filename, lineno) - if not line: - return 'That line does not exist!' - if not self.breaks.has_key(filename): - self.breaks[filename] = [] - list = self.breaks[filename] - if not lineno in list: - list.append(lineno) - bp = bdb.Breakpoint(filename, lineno, temporary, cond) diff --git a/Lib/idlelib/Delegator.py b/Lib/idlelib/Delegator.py deleted file mode 100644 index 6125591fe0dd..000000000000 --- a/Lib/idlelib/Delegator.py +++ /dev/null @@ -1,33 +0,0 @@ -class Delegator: - - # The cache is only used to be able to change delegates! - - def __init__(self, delegate=None): - self.delegate = delegate - self.__cache = {} - - def __getattr__(self, name): - attr = getattr(self.delegate, name) # May raise AttributeError - setattr(self, name, attr) - self.__cache[name] = attr - return attr - - def resetcache(self): - for key in self.__cache.keys(): - try: - delattr(self, key) - except AttributeError: - pass - self.__cache.clear() - - def cachereport(self): - keys = self.__cache.keys() - keys.sort() - print keys - - def setdelegate(self, delegate): - self.resetcache() - self.delegate = delegate - - def getdelegate(self): - return self.delegate diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py deleted file mode 100644 index 2fe18108351d..000000000000 --- a/Lib/idlelib/EditorWindow.py +++ /dev/null @@ -1,735 +0,0 @@ -# changes by dscherer@cmu.edu -# - created format and run menus -# - added silly advice dialog (apologies to Douglas Adams) -# - made Python Documentation work on Windows (requires win32api to -# do a ShellExecute(); other ways of starting a web browser are awkward) - -import sys -import os -import string -import re -import imp -from Tkinter import * -import tkSimpleDialog -import tkMessageBox - -import webbrowser -import idlever -import WindowList -from IdleConf import idleconf -import aboutDialog, textView, configDialog - -# The default tab setting for a Text widget, in average-width characters. -TK_TABWIDTH_DEFAULT = 8 - -# File menu - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> - -#$ event <> - -#$ unix -#$ unix -#$ win - -# Edit menu - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -# Help menu - -#$ event <> -#$ win -#$ unix - -#$ event <> - -# Events without menu entries - -#$ event <> -#$ win - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ unix - -class EditorWindow: - - from Percolator import Percolator - from ColorDelegator import ColorDelegator - from UndoDelegator import UndoDelegator - from IOBinding import IOBinding - import Bindings - from Tkinter import Toplevel - from MultiStatusBar import MultiStatusBar - - vars = {} - - def __init__(self, flist=None, filename=None, key=None, root=None): - edconf = idleconf.getsection('EditorWindow') - coconf = idleconf.getsection('Colors') - self.flist = flist - root = root or flist.root - self.root = root - if flist: - self.vars = flist.vars - self.menubar = Menu(root) - self.top = top = self.Toplevel(root, menu=self.menubar) - self.vbar = vbar = Scrollbar(top, name='vbar') - self.text_frame = text_frame = Frame(top) - self.text = text = Text(text_frame, name='text', padx=5, - foreground=coconf.getdef('normal-foreground'), - background=coconf.getdef('normal-background'), - highlightcolor=coconf.getdef('hilite-foreground'), - highlightbackground=coconf.getdef('hilite-background'), - insertbackground=coconf.getdef('cursor-background'), - width=edconf.getint('width'), - height=edconf.getint('height'), - wrap="none") - - self.createmenubar() - self.apply_bindings() - - self.top.protocol("WM_DELETE_WINDOW", self.close) - self.top.bind("<>", self.close_event) - text.bind("<>", self.center_insert_event) - text.bind("<>", self.help_dialog) - text.bind("<>", self.good_advice) - text.bind("<>", self.view_readme) - 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("<3>", self.right_menu_event) - if flist: - flist.inversedict[self] = key - if key: - flist.dict[key] = self - text.bind("<>", self.flist.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 - text['font'] = edconf.get('font-name'), edconf.get('font-size') - text_frame.pack(side=LEFT, fill=BOTH, expand=1) - text.pack(side=TOP, fill=BOTH, expand=1) - text.focus_set() - - self.per = per = self.Percolator(text) - if self.ispythonsource(filename): - self.color = color = self.ColorDelegator(); per.insertfilter(color) - ##print "Initial colorizer" - else: - ##print "No initial colorizer" - self.color = None - self.undo = undo = self.UndoDelegator(); per.insertfilter(undo) - self.io = io = self.IOBinding(self) - - 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) - io.set_filename_change_hook(self.filename_change_hook) - - if filename: - if os.path.exists(filename): - io.loadfile(filename) - else: - io.set_filename(filename) - - self.saved_change_hook() - - self.load_extensions() - - menu = self.menudict.get('windows') - if menu: - end = menu.index("end") - if end is None: - end = -1 - if end >= 0: - menu.add_separator() - end = end + 1 - self.wmenu_end = end - WindowList.register_callback(self.postwindowsmenu) - - # Some abstractions so IDLE extensions are cross-IDE - self.askyesno = tkMessageBox.askyesno - self.askinteger = tkSimpleDialog.askinteger - self.showerror = tkMessageBox.showerror - - if self.extensions.has_key('AutoIndent'): - self.extensions['AutoIndent'].set_indentation_params( - self.ispythonsource(filename)) - - - def set_status_bar(self): - self.status_bar = self.MultiStatusBar(self.top) - 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.bind('', self.set_line_and_column) - self.text.after_idle(self.set_line_and_column) - - def set_line_and_column(self, event=None): - line, column = string.split(self.text.index(INSERT), '.') - self.status_bar.set_label('column', 'Col: %s' % column) - self.status_bar.set_label('line', 'Ln: %s' % line) - - def wakeup(self): - if self.top.wm_state() == "iconic": - self.top.wm_deiconify() - else: - self.top.tkraise() - self.text.focus_set() - - menu_specs = [ - ("file", "_File"), - ("edit", "_Edit"), - ("format", "F_ormat"), - ("run", "_Run"), - #("settings", "_Settings"), - ("windows", "_Windows"), - ("help", "_Help"), - ] - - 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) - self.fill_menus() - - def postwindowsmenu(self): - # Only called when Windows menu exists - # XXX Actually, this Just-In-Time updating interferes badly - # XXX with the tear-off feature. It would be better to update - # XXX all Windows menus whenever the list of windows changes. - menu = self.menudict['windows'] - end = menu.index("end") - if end is None: - end = -1 - if end > self.wmenu_end: - menu.delete(self.wmenu_end+1, end) - WindowList.add_windows_to_menu(menu) - - rmenu = None - - 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") - - rmenu_specs = [ - # ("Label", "<>"), ... - ("Close", "<>"), # Example - ] - - 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 IDLEfork') - - def config_dialog(self, event=None): - configDialog.ConfigDialog(self.top,'Settings') - - def good_advice(self, event=None): - tkMessageBox.showinfo('Advice', "Don't Panic!", master=self.text) - - def view_readme(self, event=None): - fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'README.txt') - textView.TextViewer(self.top,'IDLEfork - README',fn) - - def help_dialog(self, event=None): - fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt') - textView.TextViewer(self.top,'Help',fn) - - help_url = "http://www.python.org/doc/current/" - if sys.platform[:3] == "win": - fn = os.path.dirname(__file__) - fn = os.path.join(fn, os.pardir, os.pardir, "Doc", "index.html") - fn = os.path.normpath(fn) - if os.path.isfile(fn): - help_url = fn - del fn - - def python_docs(self, event=None): - webbrowser.open(self.help_url) - - 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 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 = string.strip(name) - if not name: - name = tkSimpleDialog.askstring("Module", - "Enter the name of a Python module\n" - "to search on sys.path and open:", - parent=self.text) - if name: - name = string.strip(name) - if not name: - return - # XXX Ought to support package syntax - # XXX Ought to insert current file's directory in front of path - try: - (f, file, (suffix, mode, type)) = imp.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): - 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() - - def ispythonsource(self, filename): - if not filename: - return 1 - base, ext = os.path.splitext(os.path.basename(filename)) - if os.path.normcase(ext) in (".py", ".pyw"): - return 1 - try: - f = open(filename) - line = f.readline() - f.close() - except IOError: - return 0 - return line[:2] == '#!' and string.find(line, 'python') >= 0 - - def close_hook(self): - if self.flist: - self.flist.close_edit(self) - - 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() - if self.ispythonsource(self.io.filename): - self.addcolorizer() - else: - self.rmcolorizer() - - def addcolorizer(self): - if self.color: - return - ##print "Add colorizer" - self.per.removefilter(self.undo) - self.color = self.ColorDelegator() - self.per.insertfilter(self.color) - self.per.insertfilter(self.undo) - - def rmcolorizer(self): - if not self.color: - return - ##print "Remove colorizer" - self.per.removefilter(self.undo) - self.per.removefilter(self.color) - self.color = None - self.per.insertfilter(self.undo) - - 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() - - 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 filename - - def long_title(self): - return 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 close_event(self, event): - self.close() - - def maybesave(self): - if self.io: - return self.io.maybesave() - - def close(self): - self.top.wm_deiconify() - self.top.tkraise() - reply = self.maybesave() - if reply != "cancel": - self._close() - return reply - - def _close(self): - WindowList.unregister_callback(self.postwindowsmenu) - if self.close_hook: - self.close_hook() - self.flist = None - colorizing = 0 - self.unload_extensions() - self.io.close(); self.io = None - self.undo = None # XXX - if self.color: - colorizing = self.color.colorizing - doh = colorizing and self.top - self.color.close(doh) # Cancel colorization - self.text = None - self.vars = None - self.per.close(); self.per = None - if not colorizing: - self.top.destroy() - - 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", `name` - import traceback - traceback.print_exc() - - def get_standard_extension_names(self): - return idleconf.getextensions() - - def load_extension(self, name): - mod = __import__(name, globals(), locals(), []) - cls = getattr(mod, name) - ins = cls(self) - self.extensions[name] = ins - kdnames = ["keydefs"] - if sys.platform == 'win32': - kdnames.append("windows_keydefs") - elif sys.platform == 'mac': - kdnames.append("mac_keydefs") - else: - kdnames.append("unix_keydefs") - keydefs = {} - for kdname in kdnames: - if hasattr(ins, kdname): - keydefs.update(getattr(ins, kdname)) - if keydefs: - self.apply_bindings(keydefs) - for vevent in keydefs.keys(): - methodname = string.replace(vevent, "-", "_") - 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)) - if hasattr(ins, "menudefs"): - self.fill_menus(ins.menudefs, keydefs) - return ins - - def apply_bindings(self, keydefs=None): - if keydefs is None: - keydefs = self.Bindings.default_keydefs - text = self.text - text.keydefs = keydefs - for event, keylist in keydefs.items(): - if keylist: - apply(text.event_add, (event,) + tuple(keylist)) - - def fill_menus(self, defs=None, keydefs=None): - # Fill the menus. Menus that are absent or None in - # self.menudict are ignored. - if defs is None: - defs = self.Bindings.menudefs - if keydefs is None: - keydefs = self.Bindings.default_keydefs - menudict = self.menudict - text = self.text - for mname, itemlist in defs: - menu = menudict.get(mname) - if not menu: - continue - for item in itemlist: - if not item: - menu.add_separator() - else: - label, event = item - checkbutton = (label[:1] == '!') - if checkbutton: - label = label[1:] - underline, label = prepstr(label) - accelerator = get_accelerator(keydefs, event) - def command(text=text, event=event): - text.event_generate(event) - if checkbutton: - var = self.getrawvar(event, 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.getrawvar(name) - if var: - return var.get() - - def setvar(self, name, value, vartype=None): - var = self.getrawvar(name, vartype) - if var: - var.set(value) - - def getrawvar(self, name, vartype=None): - var = self.vars.get(name) - if not var and vartype: - self.vars[name] = var = vartype(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): - 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): - text = self.text - if self.get_tabwidth() != newtabwidth: - pixels = text.tk.call("font", "measure", text["font"], - "-displayof", text.master, - "n" * newtabwidth) - text.configure(tabs=pixels) - -def prepstr(s): - # Helper to extract the underscore from a string, e.g. - # prepstr("Co_py") returns (2, "Copy"). - i = string.find(s, '_') - if i >= 0: - s = s[:i] + s[i+1:] - return i, s - - -keynames = { - 'bracketleft': '[', - 'bracketright': ']', - 'slash': '/', -} - -def get_accelerator(keydefs, event): - keylist = keydefs.get(event) - if not keylist: - return "" - s = keylist[0] - s = re.sub(r"-[a-z]\b", lambda m: string.upper(m.group()), s) - s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s) - s = re.sub("Key-", "", s) - s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu - s = re.sub("Control-", "Ctrl-", s) - s = re.sub("-", "+", s) - s = re.sub("><", " ", s) - s = re.sub("<", "", s) - s = re.sub(">", "", s) - return s - - -def fixwordbreaks(root): - # Make sure that Tk's double-click and next/previous word - # operations use our definition of a word (i.e. an identifier) - tk = root.tk - tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded - tk.call('set', 'tcl_wordchars', '[a-zA-Z0-9_]') - tk.call('set', 'tcl_nonwordchars', '[^a-zA-Z0-9_]') - - -def test(): - root = Tk() - fixwordbreaks(root) - root.withdraw() - if sys.argv[1:]: - filename = sys.argv[1] - else: - filename = None - edit = EditorWindow(root=root, filename=filename) - edit.set_close_hook(root.quit) - root.mainloop() - root.destroy() - -if __name__ == '__main__': - test() diff --git a/Lib/idlelib/ExecBinding.py b/Lib/idlelib/ExecBinding.py deleted file mode 100644 index 67b08220d9df..000000000000 --- a/Lib/idlelib/ExecBinding.py +++ /dev/null @@ -1,198 +0,0 @@ -"""Extension to execute a script in a separate process - -David Scherer - - The ExecBinding module, a replacement for ScriptBinding, executes - programs in a separate process. Unlike previous versions, this version - communicates with the user process via an RPC protocol (see the 'protocol' - module). The user program is loaded by the 'loader' and 'Remote' - modules. Its standard output and input are directed back to the - ExecBinding class through the RPC mechanism and implemented here. - - A "stop program" command is provided and bound to control-break. Closing - the output window also stops the running program. -""" - -import sys -import os -import imp -import OutputWindow -import protocol -import spawn -import traceback -import tempfile - -# Find Python and the loader. This should be done as early in execution -# as possible, because if the current directory or sys.path is changed -# it may no longer be possible to get correct paths for these things. - -pyth_exe = spawn.hardpath( sys.executable ) -load_py = spawn.hardpath( imp.find_module("loader")[1] ) - -# The following mechanism matches loaders up with ExecBindings that are -# trying to load something. - -waiting_for_loader = [] - -def loader_connect(client, addr): - if waiting_for_loader: - a = waiting_for_loader.pop(0) - try: - return a.connect(client, addr) - except: - return loader_connect(client,addr) - -protocol.publish('ExecBinding', loader_connect) - -class ExecBinding: - keydefs = { - '<>': [''], - '<>': [''], #'' - } - - menudefs = [ - ('run', [None, - ('Run program', '<>'), - ('Stop program', '<>'), - ] - ), - ] - - delegate = 1 - - def __init__(self, editwin): - self.editwin = editwin - self.client = None - self.temp = [] - - if not hasattr(editwin, 'source_window'): - self.delegate = 0 - self.output = OutputWindow.OnDemandOutputWindow(editwin.flist) - self.output.close_hook = self.stopProgram - self.output.source_window = editwin - else: - if (self.editwin.source_window and - self.editwin.source_window.extensions.has_key('ExecBinding') and - not self.editwin.source_window.extensions['ExecBinding'].delegate): - delegate = self.editwin.source_window.extensions['ExecBinding'] - self.run_complete_script_event = delegate.run_complete_script_event - self.stop_execution_event = delegate.stop_execution_event - - def __del__(self): - self.stopProgram() - - def stop_execution_event(self, event): - if self.client: - self.stopProgram() - self.write('\nProgram stopped.\n','stderr') - - def run_complete_script_event(self, event): - filename = self.getfilename() - if not filename: return - filename = os.path.abspath(filename) - - self.stopProgram() - - self.commands = [ ('run', filename) ] - waiting_for_loader.append(self) - spawn.spawn( pyth_exe, load_py ) - - def connect(self, client, addr): - # Called by loader_connect() above. It is remotely possible that - # we get connected to two loaders if the user is running the - # program repeatedly in a short span of time. In this case, we - # simply return None, refusing to connect and letting the redundant - # loader die. - if self.client: return None - - self.client = client - client.set_close_hook( self.connect_lost ) - - title = self.editwin.short_title() - if title: - self.output.set_title(title + " Output") - else: - self.output.set_title("Output") - self.output.write('\n',"stderr") - self.output.scroll_clear() - - return self - - def connect_lost(self): - # Called by the client's close hook when the loader closes its - # socket. - - # We print a disconnect message only if the output window is already - # open. - if self.output.owin and self.output.owin.text: - self.output.owin.interrupt() - self.output.write("\nProgram disconnected.\n","stderr") - - for t in self.temp: - try: - os.remove(t) - except: - pass - self.temp = [] - self.client = None - - def get_command(self): - # Called by Remote to find out what it should be executing. - # Later this will be used to implement debugging, interactivity, etc. - if self.commands: - return self.commands.pop(0) - return ('finish',) - - def program_exception(self, type, value, tb, first, last): - if type == SystemExit: return 0 - - for i in range(len(tb)): - filename, lineno, name, line = tb[i] - if filename in self.temp: - filename = 'Untitled' - tb[i] = filename, lineno, name, line - - list = traceback.format_list(tb[first:last]) - exc = traceback.format_exception_only( type, value ) - - self.write('Traceback (innermost last)\n', 'stderr') - for i in (list+exc): - self.write(i, 'stderr') - - self.commands = [] - return 1 - - def write(self, text, tag): - self.output.write(text,tag) - - def readline(self): - return self.output.readline() - - def stopProgram(self): - if self.client: - self.client.close() - self.client = None - - def getfilename(self): - # Save all files which have been named, because they might be modules - for edit in self.editwin.flist.inversedict.keys(): - if edit.io and edit.io.filename and not edit.get_saved(): - edit.io.save(None) - - # Experimental: execute unnamed buffer - if not self.editwin.io.filename: - filename = os.path.normcase(os.path.abspath(tempfile.mktemp())) - self.temp.append(filename) - if self.editwin.io.writefile(filename): - return filename - - # If the file isn't save, we save it. If it doesn't have a filename, - # the user will be prompted. - if self.editwin.io and not self.editwin.get_saved(): - self.editwin.io.save(None) - - # If the file *still* isn't saved, we give up. - if not self.editwin.get_saved(): - return - - return self.editwin.io.filename diff --git a/Lib/idlelib/FileList.py b/Lib/idlelib/FileList.py deleted file mode 100644 index e01ce3c47cc1..000000000000 --- a/Lib/idlelib/FileList.py +++ /dev/null @@ -1,146 +0,0 @@ -# changes by dscherer@cmu.edu -# - FileList.open() takes an optional 3rd parameter action, which is -# called instead of creating a new EditorWindow. This enables -# things like 'open in same window'. - -import os -from Tkinter import * -import tkMessageBox - -import WindowList - -#$ event <> -#$ win -#$ unix - -# (This is labeled as 'Exit'in the File menu) -#$ event <> -#$ win -#$ unix - -class FileList: - - from EditorWindow import EditorWindow - EditorWindow.Toplevel = WindowList.ListedToplevel # XXX Patch it! - - def __init__(self, root): - self.root = root - self.dict = {} - self.inversedict = {} - self.vars = {} # For EditorWindow.getrawvar (shared Tcl variables) - - def open(self, filename, action=None): - assert filename - filename = self.canonize(filename) - if os.path.isdir(filename): - tkMessageBox.showerror( - "Is A Directory", - "The path %s is a directory." % `filename`, - master=self.root) - return None - key = os.path.normcase(filename) - if self.dict.has_key(key): - edit = self.dict[key] - edit.wakeup() - return edit - if not os.path.exists(filename): - tkMessageBox.showinfo( - "New File", - "Opening non-existent file %s" % `filename`, - master=self.root) - if action is None: - return self.EditorWindow(self, filename, key) - else: - return action(filename) - - def gotofileline(self, filename, lineno=None): - edit = self.open(filename) - if edit is not None and lineno is not None: - edit.gotoline(lineno) - - def new(self): - return self.EditorWindow(self) - - def new_callback(self, event): - self.new() - return "break" - - def close_all_callback(self, event): - for edit in self.inversedict.keys(): - reply = edit.close() - if reply == "cancel": - break - return "break" - - def close_edit(self, edit): - try: - key = self.inversedict[edit] - except KeyError: - print "Don't know this EditorWindow object. (close)" - return - if key: - del self.dict[key] - del self.inversedict[edit] - if not self.inversedict: - self.root.quit() - - def filename_changed_edit(self, edit): - edit.saved_change_hook() - try: - key = self.inversedict[edit] - except KeyError: - print "Don't know this EditorWindow object. (rename)" - return - filename = edit.io.filename - if not filename: - if key: - del self.dict[key] - self.inversedict[edit] = None - return - filename = self.canonize(filename) - newkey = os.path.normcase(filename) - if newkey == key: - return - if self.dict.has_key(newkey): - conflict = self.dict[newkey] - self.inversedict[conflict] = None - tkMessageBox.showerror( - "Name Conflict", - "You now have multiple edit windows open for %s" % `filename`, - master=self.root) - self.dict[newkey] = edit - self.inversedict[edit] = 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(): - from EditorWindow import fixwordbreaks - import sys - root = Tk() - fixwordbreaks(root) - root.withdraw() - flist = FileList(root) - if sys.argv[1:]: - for filename in sys.argv[1:]: - flist.open(filename) - else: - flist.new() - if flist.inversedict: - root.mainloop() - -if __name__ == '__main__': - _test() diff --git a/Lib/idlelib/FormatParagraph.py b/Lib/idlelib/FormatParagraph.py deleted file mode 100644 index 498e2efbad31..000000000000 --- a/Lib/idlelib/FormatParagraph.py +++ /dev/null @@ -1,155 +0,0 @@ -# Extension to format a paragraph - -# Does basic, standard text formatting, and also understands Python -# comment blocks. Thus, for editing Python source code, this -# extension is really only suitable for reformatting these comment -# blocks or triple-quoted strings. - -# Known problems with comment reformatting: -# * If there is a selection marked, and the first line of the -# selection is not complete, the block will probably not be detected -# as comments, and will have the normal "text formatting" rules -# applied. -# * If a comment block has leading whitespace that mixes tabs and -# spaces, they will not be considered part of the same block. -# * Fancy comments, like this bulleted list, arent handled :-) - -import string -import re - -class FormatParagraph: - - menudefs = [ - ('format', [ # /s/edit/format dscherer@cmu.edu - ('Format Paragraph', '<>'), - ]) - ] - - keydefs = { - '<>': [''], - } - - unix_keydefs = { - '<>': [''], - } - - def __init__(self, editwin): - self.editwin = editwin - - def close(self): - self.editwin = None - - def format_paragraph_event(self, event): - text = self.editwin.text - first, last = self.editwin.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")) - if comment_header: - # Reformat the comment lines - convert to text sans header. - lines = string.split(data, "\n") - lines = map(lambda st, l=len(comment_header): st[l:], lines) - data = string.join(lines, "\n") - # Reformat to 70 chars or a 20 char width, whichever is greater. - format_width = max(70-len(comment_header), 20) - newdata = reformat_paragraph(data, format_width) - # re-split and re-insert the comment header. - newdata = string.split(newdata, "\n") - # If the block ends in a \n, we dont want the comment - # prefix inserted after it. (Im not sure it makes sense to - # reformat a comment block that isnt made of complete - # lines, but whatever!) Can't think of a clean soltution, - # so we hack away - block_suffix = "" - if not newdata[-1]: - block_suffix = "\n" - newdata = newdata[:-1] - builder = lambda item, prefix=comment_header: prefix+item - newdata = string.join(map(builder, newdata), '\n') + block_suffix - else: - # Just a normal text format - newdata = reformat_paragraph(data) - text.tag_remove("sel", "1.0", "end") - if newdata != data: - text.mark_set("insert", first) - text.undo_block_start() - text.delete(first, last) - text.insert(first, newdata) - text.undo_block_stop() - else: - text.mark_set("insert", last) - text.see("insert") - -def find_paragraph(text, mark): - lineno, col = map(int, string.split(mark, ".")) - line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) - while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) - first_lineno = lineno - comment_header = get_comment_header(line) - comment_header_len = len(comment_header) - while get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno + 1 - line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) - last = "%d.0" % lineno - # Search back to beginning of paragraph - lineno = first_lineno - 1 - line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) - while lineno > 0 and \ - get_comment_header(line)==comment_header and \ - not is_all_white(line[comment_header_len:]): - lineno = lineno - 1 - line = text.get("%d.0" % lineno, "%d.0 lineend" % lineno) - first = "%d.0" % (lineno+1) - return first, last, comment_header, text.get(first, last) - -def reformat_paragraph(data, limit=70): - lines = string.split(data, "\n") - i = 0 - n = len(lines) - while i < n and is_all_white(lines[i]): - i = i+1 - if i >= n: - return data - indent1 = get_indent(lines[i]) - if i+1 < n and not is_all_white(lines[i+1]): - indent2 = get_indent(lines[i+1]) - else: - indent2 = indent1 - new = lines[:i] - partial = indent1 - while i < n and not is_all_white(lines[i]): - # XXX Should take double space after period (etc.) into account - words = re.split("(\s+)", lines[i]) - for j in range(0, len(words), 2): - word = words[j] - if not word: - continue # Can happen when line ends in whitespace - if len(string.expandtabs(partial + word)) > limit and \ - partial != indent1: - new.append(string.rstrip(partial)) - partial = indent2 - partial = partial + word + " " - if j+1 < len(words) and words[j+1] != " ": - partial = partial + " " - i = i+1 - new.append(string.rstrip(partial)) - # XXX Should reformat remaining paragraphs as well - new.extend(lines[i:]) - return string.join(new, "\n") - -def is_all_white(line): - return re.match(r"^\s*$", line) is not None - -def get_indent(line): - return re.match(r"^(\s*)", line).group() - -def get_comment_header(line): - m = re.match(r"^(\s*#*)", line) - if m is None: return "" - return m.group(1) diff --git a/Lib/idlelib/FrameViewer.py b/Lib/idlelib/FrameViewer.py deleted file mode 100644 index 2ce0935ba384..000000000000 --- a/Lib/idlelib/FrameViewer.py +++ /dev/null @@ -1,38 +0,0 @@ -from repr import Repr -from Tkinter import * - -class FrameViewer: - - def __init__(self, root, frame): - self.root = root - self.frame = frame - self.top = Toplevel(self.root) - self.repr = Repr() - self.repr.maxstring = 60 - self.load_variables() - - def load_variables(self): - row = 0 - if self.frame.f_locals is not self.frame.f_globals: - l = Label(self.top, text="Local Variables", - borderwidth=2, relief="raised") - l.grid(row=row, column=0, columnspan=2, sticky="ew") - row = self.load_names(self.frame.f_locals, row+1) - l = Label(self.top, text="Global Variables", - borderwidth=2, relief="raised") - l.grid(row=row, column=0, columnspan=2, sticky="ew") - row = self.load_names(self.frame.f_globals, row+1) - - def load_names(self, dict, row): - names = dict.keys() - names.sort() - for name in names: - value = dict[name] - svalue = self.repr.repr(value) - l = Label(self.top, text=name) - l.grid(row=row, column=0, sticky="w") - l = Entry(self.top, width=60, borderwidth=0) - l.insert(0, svalue) - l.grid(row=row, column=1, sticky="w") - row = row+1 - return row diff --git a/Lib/idlelib/GrepDialog.py b/Lib/idlelib/GrepDialog.py deleted file mode 100644 index 61c77c349389..000000000000 --- a/Lib/idlelib/GrepDialog.py +++ /dev/null @@ -1,135 +0,0 @@ -import string -import os -import re -import fnmatch -import sys -from Tkinter import * -import tkMessageBox -import SearchEngine -from SearchDialogBase import SearchDialogBase - -def grep(text, io=None, flist=None): - root = text._root() - engine = SearchEngine.get(root) - if not hasattr(engine, "_grepdialog"): - engine._grepdialog = GrepDialog(root, engine, flist) - dialog = engine._grepdialog - dialog.open(io) - -class GrepDialog(SearchDialogBase): - - title = "Find in Files Dialog" - icon = "Grep" - needwrapbutton = 0 - - def __init__(self, root, engine, flist): - SearchDialogBase.__init__(self, root, engine) - self.flist = flist - self.globvar = StringVar(root) - self.recvar = BooleanVar(root) - - def open(self, io=None): - SearchDialogBase.open(self, None) - if io: - path = io.filename or "" - else: - path = "" - dir, base = os.path.split(path) - head, tail = os.path.splitext(base) - if not tail: - tail = ".py" - self.globvar.set(os.path.join(dir, "*" + tail)) - - def create_entries(self): - SearchDialogBase.create_entries(self) - 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, - text="Recurse down subdirectories") - btn.pack(side="top", fill="both") - btn.select() - - 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() - if not prog: - return - path = self.globvar.get() - if not path: - self.top.bell() - return - from OutputWindow import OutputWindow - save = sys.stdout - try: - sys.stdout = OutputWindow(self.flist) - self.grep_it(prog, path) - finally: - sys.stdout = save - - def grep_it(self, prog, path): - dir, base = os.path.split(path) - list = self.findfiles(dir, base, self.recvar.get()) - list.sort() - self.close() - pat = self.engine.getpat() - print "Searching %s in %s ..." % (`pat`, path) - hits = 0 - for fn in list: - try: - f = open(fn) - except IOError, msg: - print msg - continue - lineno = 0 - while 1: - block = f.readlines(100000) - if not block: - break - for line in block: - lineno = lineno + 1 - if line[-1:] == '\n': - line = line[:-1] - if prog.search(line): - sys.stdout.write("%s: %s: %s\n" % (fn, lineno, line)) - hits = hits + 1 - if hits: - if hits == 1: - s = "" - else: - s = "s" - print "Found", hits, "hit%s." % s - print "(Hint: right-click to open locations.)" - else: - print "No hits." - - def findfiles(self, dir, base, rec): - try: - names = os.listdir(dir or os.curdir) - except os.error, msg: - print msg - return [] - list = [] - subdirs = [] - for name in names: - fn = os.path.join(dir, name) - if os.path.isdir(fn): - subdirs.append(fn) - else: - if fnmatch.fnmatch(name, base): - list.append(fn) - if rec: - 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() diff --git a/Lib/idlelib/INSTALL.txt b/Lib/idlelib/INSTALL.txt deleted file mode 100644 index fcf42e129b48..000000000000 --- a/Lib/idlelib/INSTALL.txt +++ /dev/null @@ -1,58 +0,0 @@ -IDLEfork INSTALL notes -====================== - -The emphasis in IDLEfork is now for the project to be able to be run -directly from the unpacked source directory. This is to enable easy testing -of (and hacking on) IDLEfork, and will also prevent interfering with the -stable Python IDLE set up in any way. - -To install IDLEfork just unpack the archive into its own directory wherever -you like. To run IDLEfork just go to the directory you unpacked IDLEfork -into and then run 'python idle.py' in an xterm under unix/linux, or -'idle.pyw' under windows 98/2000. Remember that IDLEfork 0.8.1 and greater -require python 2.1 or greater. - -See README.txt and NEWS.txt for more details on this version of IDLEfork. - - -INSTALLATION notes from IDLE fork 0.7.1 : -========================================= - -IDLE Fork Installation on Linux: - -Until the tarball is released, you must download a CVS copy. An excellent -place for it is - -/usr/local/src/PythonX.X/Tools/idlefork, assuming that's where your Python -source is located. Put the correct version in for X.X . - -# cd /usr/local/src/PythonX.X/Tools - -Now do the CVS login and checkout: - -# cvs -d:pserver:anonymous@cvs.idlefork.sourceforge.net:/cvsroot/idlefork login - -Type an for the password. - -# cvs -z3 -d:pserver:anonymous@cvs.idlefork.sourceforge.net:/cvsroot/idlefork \ - -d idlefork checkout idle - -The -d option to checkout puts the files in an idlefork directory, so you don't -step on "official" idle. - -# cd idlefork -# su to root - -# python setup.py install - -# echo "idle" > /usr/local/lib/pythonX.X/site-packages.pth - -This last is necessary so idle can find itself. I hope we can create/append -this file via setup.py at some point, but it needs to be done manually now, and -it only needs to be done once (unless you totally remove and reinstall python -itself). - -# exit from root - -NOTE that the above procedure will install idlefork IDLE on top of any -"official" IDLE that may be already installed. diff --git a/Lib/idlelib/IOBinding.py b/Lib/idlelib/IOBinding.py deleted file mode 100644 index 4875d111a775..000000000000 --- a/Lib/idlelib/IOBinding.py +++ /dev/null @@ -1,254 +0,0 @@ -# changes by dscherer@cmu.edu -# - IOBinding.open() replaces the current window with the opened file, -# if the current window is both unmodified and unnamed -# - IOBinding.loadfile() interprets Windows, UNIX, and Macintosh -# end-of-line conventions, instead of relying on the standard library, -# which will only understand the local convention. - -import os -import tkFileDialog -import tkMessageBox -import re - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - - -class IOBinding: - - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text - self.__id_open = self.text.bind("<>", self.open) - self.__id_save = self.text.bind("<>", self.save) - self.__id_saveas = self.text.bind("<>", - self.save_as) - self.__id_savecopy = self.text.bind("<>", - self.save_a_copy) - - def close(self): - # Undo command bindings - self.text.unbind("<>", self.__id_open) - self.text.unbind("<>", self.__id_save) - self.text.unbind("<>",self.__id_saveas) - self.text.unbind("<>", self.__id_savecopy) - # Break cycles - self.editwin = None - self.text = None - self.filename_change_hook = None - - def get_saved(self): - return self.editwin.get_saved() - - def set_saved(self, flag): - self.editwin.set_saved(flag) - - def reset_undo(self): - self.editwin.reset_undo() - - filename_change_hook = None - - def set_filename_change_hook(self, hook): - self.filename_change_hook = hook - - filename = None - - def set_filename(self, filename): - self.filename = filename - self.set_saved(1) - if self.filename_change_hook: - self.filename_change_hook() - - def open(self, event): - if self.editwin.flist: - filename = self.askopenfile() - if filename: - # if the current window has no filename and hasn't been - # modified, we replace it's contents (no loss). Otherwise - # we open a new window. - if not self.filename and self.get_saved(): - self.editwin.flist.open(filename, self.loadfile) - else: - self.editwin.flist.open(filename) - else: - self.text.focus_set() - - return "break" - # Code for use outside IDLE: - if self.get_saved(): - reply = self.maybesave() - if reply == "cancel": - self.text.focus_set() - return "break" - filename = self.askopenfile() - if filename: - self.loadfile(filename) - else: - self.text.focus_set() - return "break" - - def loadfile(self, filename): - try: - # open the file in binary mode so that we can handle - # end-of-line convention ourselves. - f = open(filename,'rb') - chars = f.read() - f.close() - except IOError, msg: - tkMessageBox.showerror("I/O Error", str(msg), master=self.text) - return 0 - - # We now convert all end-of-lines to '\n's - eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac) - chars = re.compile( eol ).sub( r"\n", chars ) - - self.text.delete("1.0", "end") - self.set_filename(None) - self.text.insert("1.0", chars) - self.reset_undo() - self.set_filename(filename) - self.text.mark_set("insert", "1.0") - self.text.see("insert") - return 1 - - def maybesave(self): - if self.get_saved(): - return "yes" - message = "Do you want to save %s before closing?" % ( - self.filename or "this untitled document") - m = tkMessageBox.Message( - title="Save On Close", - message=message, - icon=tkMessageBox.QUESTION, - type=tkMessageBox.YESNOCANCEL, - master=self.text) - reply = m.show() - if reply == "yes": - self.save(None) - if not self.get_saved(): - reply = "cancel" - self.text.focus_set() - return reply - - def save(self, event): - if not self.filename: - self.save_as(event) - else: - if self.writefile(self.filename): - self.set_saved(1) - self.text.focus_set() - return "break" - - def save_as(self, event): - filename = self.asksavefile() - if filename: - if self.writefile(filename): - self.set_filename(filename) - self.set_saved(1) - self.text.focus_set() - return "break" - - def save_a_copy(self, event): - filename = self.asksavefile() - if filename: - self.writefile(filename) - self.text.focus_set() - return "break" - - def writefile(self, filename): - self.fixlastline() - try: - f = open(filename, "w") - chars = self.text.get("1.0", "end-1c") - f.write(chars) - f.close() - ## print "saved to", `filename` - return 1 - except IOError, msg: - tkMessageBox.showerror("I/O Error", str(msg), - master=self.text) - return 0 - - def fixlastline(self): - c = self.text.get("end-2c") - if c != '\n': - self.text.insert("end-1c", "\n") - - opendialog = None - savedialog = None - - filetypes = [ - ("Python and text files", "*.py *.pyw *.txt", "TEXT"), - ("All text files", "*", "TEXT"), - ("All files", "*"), - ] - - def askopenfile(self): - dir, base = self.defaultfilename("open") - if not self.opendialog: - self.opendialog = tkFileDialog.Open(master=self.text, - filetypes=self.filetypes) - return self.opendialog.show(initialdir=dir, initialfile=base) - - def defaultfilename(self, mode="open"): - if self.filename: - return os.path.split(self.filename) - else: - try: - pwd = os.getcwd() - except os.error: - pwd = "" - return pwd, "" - - def asksavefile(self): - dir, base = self.defaultfilename("save") - if not self.savedialog: - self.savedialog = tkFileDialog.SaveAs(master=self.text, - filetypes=self.filetypes) - return self.savedialog.show(initialdir=dir, initialfile=base) - - -def test(): - root = Tk() - class MyEditWin: - def __init__(self, text): - self.text = text - self.flist = None - self.text.bind("", self.open) - self.text.bind("", self.save) - self.text.bind("", self.save_as) - self.text.bind("", self.save_a_copy) - def get_saved(self): return 0 - def set_saved(self, flag): pass - def reset_undo(self): pass - def open(self, event): - self.text.event_generate("<>") - def save(self, event): - self.text.event_generate("<>") - def save_as(self, event): - self.text.event_generate("<>") - def save_a_copy(self, event): - self.text.event_generate("<>") - text = Text(root) - text.pack() - text.focus_set() - editwin = MyEditWin(text) - io = IOBinding(editwin) - root.mainloop() - -if __name__ == "__main__": - from Tkinter import * - test() diff --git a/Lib/idlelib/Icons/folder.gif b/Lib/idlelib/Icons/folder.gif deleted file mode 100644 index effe8dc8a00681b3f1c1f5bdd7808a924e3b9b15..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 120 zc-nLKbhEHb2o4-Lp!k!8k%57oK?lSK zsdZqstq4D}EI?FXl_(>3(z3teaQY;5HC^j%~%Nn&-nFaQA7B^xgQ diff --git a/Lib/idlelib/Icons/openfolder.gif b/Lib/idlelib/Icons/openfolder.gif deleted file mode 100644 index 24aea1bebe8413427326f964ae8b6f9a29641029..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 125 zc-nLKbhEHb6ky7fiA{5+57r9a@@xaKl9H#tW>S8=RaE bzBro0!*Kd>PT20+wB2&-H*#hPF<1it%q=nK diff --git a/Lib/idlelib/Icons/plusnode.gif b/Lib/idlelib/Icons/plusnode.gif deleted file mode 100644 index 13ace90eb3269a14e363ca3646b08c4c3581d026..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 79 zc-nLKbhEHb_4N!43>", self.history_prev) - text.bind("<>", self.history_next) - - def history_next(self, event): - self.history_do(0) - return "break" - - def history_prev(self, event): - self.history_do(1) - return "break" - - def _get_source(self, start, end): - # Get source code from start index to end index. Lines in the - # text control may be separated by sys.ps2 . - lines = string.split(self.text.get(start, end), self.output_sep) - return string.join(lines, "\n") - - def _put_source(self, where, source): - output = string.join(string.split(source, "\n"), self.output_sep) - self.text.insert(where, output) - - def history_do(self, reverse): - nhist = len(self.history) - pointer = self.history_pointer - prefix = self.history_prefix - if pointer is not None and prefix is not None: - if self.text.compare("insert", "!=", "end-1c") or \ - self._get_source("iomark", "end-1c") != self.history[pointer]: - pointer = prefix = None - if pointer is None or prefix is None: - prefix = self._get_source("iomark", "end-1c") - if reverse: - pointer = nhist - else: - pointer = -1 - nprefix = len(prefix) - while 1: - if reverse: - pointer = pointer - 1 - else: - pointer = pointer + 1 - if pointer < 0 or pointer >= nhist: - self.text.bell() - if self._get_source("iomark", "end-1c") != prefix: - self.text.delete("iomark", "end-1c") - self._put_source("iomark", prefix) - pointer = prefix = None - break - item = self.history[pointer] - if item[:nprefix] == prefix and len(item) > nprefix: - self.text.delete("iomark", "end-1c") - self._put_source("iomark", item) - break - self.text.mark_set("insert", "end-1c") - self.text.see("insert") - self.text.tag_remove("sel", "1.0", "end") - self.history_pointer = pointer - self.history_prefix = prefix - - def history_store(self, source): - source = string.strip(source) - if len(source) > 2: - # avoid duplicates - try: - self.history.remove(source) - except ValueError: - pass - self.history.append(source) - self.history_pointer = None - self.history_prefix = None - - def recall(self, s): - s = string.strip(s) - self.text.tag_remove("sel", "1.0", "end") - self.text.delete("iomark", "end-1c") - self.text.mark_set("insert", "end-1c") - self.text.insert("insert", s) - self.text.see("insert") diff --git a/Lib/idlelib/LICENSE.txt b/Lib/idlelib/LICENSE.txt deleted file mode 100644 index f7a839585b97..000000000000 --- a/Lib/idlelib/LICENSE.txt +++ /dev/null @@ -1,50 +0,0 @@ -To apply this license to IDLE or IDLEfork, read 'IDLE' or 'IDLEfork' -for every occurence of 'Python 2.1.1' in the text below. - -PSF LICENSE AGREEMENT ---------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using Python 2.1.1 software in source or binary form and its -associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 2.1.1 -alone or in any derivative version, provided, however, that PSF's -License Agreement and PSF's notice of copyright, i.e., "Copyright (c) -2001 Python Software Foundation; All Rights Reserved" are retained in -Python 2.1.1 alone or in any derivative version prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 2.1.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 2.1.1. - -4. PSF is making Python 2.1.1 available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.1.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -2.1.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.1.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python 2.1.1, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. diff --git a/Lib/idlelib/MultiScrolledLists.py b/Lib/idlelib/MultiScrolledLists.py deleted file mode 100644 index 6c140df45148..000000000000 --- a/Lib/idlelib/MultiScrolledLists.py +++ /dev/null @@ -1,138 +0,0 @@ -# One or more ScrolledLists with HSeparators between them. -# There is a hierarchical relationship between them: -# the right list displays the substructure of the selected item -# in the left list. - -import string -from Tkinter import * -from WindowList import ListedToplevel -from Separator import HSeparator -from ScrolledList import ScrolledList - -class MultiScrolledLists: - - def __init__(self, root, nlists=2): - assert nlists >= 1 - self.root = root - self.nlists = nlists - self.path = [] - # create top - self.top = top = ListedToplevel(root) - top.protocol("WM_DELETE_WINDOW", self.close) - top.bind("", self.close) - self.settitle() - # create frames and separators in between - self.frames = [] - self.separators = [] - last = top - for i in range(nlists-1): - sepa = HSeparator(last) - self.separators.append(sepa) - frame, last = sepa.parts() - self.frames.append(frame) - self.frames.append(last) - # create labels and lists - self.labels = [] - self.lists = [] - for i in range(nlists): - frame = self.frames[i] - label = Label(frame, text=self.subtitle(i), - relief="groove", borderwidth=2) - label.pack(fill="x") - self.labels.append(label) - list = ScrolledList(frame, width=self.width(i), - height=self.height(i)) - self.lists.append(list) - list.on_select = \ - lambda index, i=i, self=self: self.on_select(index, i) - list.on_double = \ - lambda index, i=i, self=self: self.on_double(index, i) - # fill leftmost list (rest get filled on demand) - self.fill(0) - # XXX one after_idle isn't enough; two are... - top.after_idle(self.call_pack_propagate_1) - - def call_pack_propagate_1(self): - self.top.after_idle(self.call_pack_propagate) - - def call_pack_propagate(self): - for frame in self.frames: - frame.pack_propagate(0) - - def close(self, event=None): - self.top.destroy() - - def settitle(self): - short = self.shorttitle() - long = self.longtitle() - if short and long: - title = short + " - " + long - elif short: - title = short - elif long: - title = long - else: - title = "Untitled" - icon = short or long or title - self.top.wm_title(title) - self.top.wm_iconname(icon) - - def longtitle(self): - # override this - return "Multi Scrolled Lists" - - def shorttitle(self): - # override this - return None - - def width(self, i): - # override this - return 20 - - def height(self, i): - # override this - return 10 - - def subtitle(self, i): - # override this - return "Column %d" % i - - def fill(self, i): - for k in range(i, self.nlists): - self.lists[k].clear() - self.labels[k].configure(text=self.subtitle(k)) - list = self.lists[i] - l = self.items(i) - for s in l: - list.append(s) - - def on_select(self, index, i): - item = self.lists[i].get(index) - del self.path[i:] - self.path.append(item) - if i+1 < self.nlists: - self.fill(i+1) - - def items(self, i): - # override this - l = [] - for k in range(10): - s = str(k) - if i > 0: - s = self.path[i-1] + "." + s - l.append(s) - return l - - def on_double(self, index, i): - pass - - -def main(): - root = Tk() - quit = Button(root, text="Exit", command=root.destroy) - quit.pack() - MultiScrolledLists(root, 4) - root.mainloop() - -if __name__ == "__main__": - main() diff --git a/Lib/idlelib/MultiStatusBar.py b/Lib/idlelib/MultiStatusBar.py deleted file mode 100644 index dd6d04145f24..000000000000 --- a/Lib/idlelib/MultiStatusBar.py +++ /dev/null @@ -1,32 +0,0 @@ -from Tkinter import * - -class MultiStatusBar(Frame): - - def __init__(self, master=None, **kw): - if master is None: - master = Tk() - apply(Frame.__init__, (self, master), kw) - self.labels = {} - - 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.pack(side=side) - self.labels[name] = label - else: - label = self.labels[name] - label.config(text=text) - -def _test(): - b = Frame() - c = Text(b) - c.pack(side=TOP) - a = MultiStatusBar(b) - a.set_label("one", "hello") - a.set_label("two", "world") - a.pack(side=BOTTOM, fill=X) - b.pack() - b.mainloop() - -if __name__ == '__main__': - _test() diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt deleted file mode 100644 index 3cff0472f7ca..000000000000 --- a/Lib/idlelib/NEWS.txt +++ /dev/null @@ -1,173 +0,0 @@ -IDLEfork NEWS -============= -(For a more detailed change log, see the file ChangeLog.) ---------------------------------------------------------- - - -IDLEfork 0.8.1 (22 JUL 2001) ----------------------------- -New tarball released as a result of the 'revitalisation' of the IDLEfork -project. - -This release requires python 2.1 or better. Compatability with earlier -versions of python (especially ancient ones like 1.5x) is no longer -a priority in IDLEfork development. - -This release is based on a merging of the earlier IDLE fork work with -current cvs IDLE (post IDLE version 0.8), with some minor additional -coding by Kurt B. Kaiser and Stephen M. Gava. - -This release is basically functional but also contains some known -breakages, particularly with running things from the shell window. Also -the debugger is not working, but I believe this was the case with the -previous IDLE fork release (0.7.1) as well. - -This release is being made now to mark the point at which IDLEfork is -launching into a new stage of development. - -IDLEfork CVS will now be branched to enable further development and -exploration of the two "execution in a remote process" patches submitted -by David Scherer (David's is currently in IDLEfork) and GvR, while -stabilisation and development of less heavyweight improvements (like -user customisation) can continue on the trunk. - - -IDLE fork 0.7.1 (15 AUG 2000) ------------------------------ -First project tarball released. - -This was the first release of IDLE fork, which at this stage was a -combination of IDLE 0.5 and the VPython idle fork, with additional -changes coded by David Scherer, Peter Schneider-Kamp and -Nicholas Riley. - - -original IDLE NEWS.txt : -======================== - -New in IDLE 0.5 (2/15/2000) -------------------------- - -Tons of stuff, much of it contributed by Tim Peters and Mark Hammond: - -- Status bar, displaying current line/column (Moshe Zadka). - -- Better stack viewer, using tree widget. (XXX Only used by Stack -Viewer menu, not by the debugger.) - -- Format paragraph now recognizes Python block comments and reformats -them correctly (MH) - -- New version of pyclbr.py parses top-level functions and understands -much more of Python's syntax; this is reflected in the class and path -browsers (TP) - -- Much better auto-indent; knows how to indent the insides of -multi-line statements (TP) - -- Call tip window pops up when you type the name of a known function -followed by an open parenthesis. Hit ESC or click elsewhere in the -window to close the tip window (MH) - -- Comment out region now inserts ## to make it stand out more (TP) - -- New path and class browsers based on a tree widget that looks -familiar to Windows users - -- Reworked script running commands to be more intuitive: I/O now -always goes to the *Python Shell* window, and raw_input() works -correctly. You use F5 to import/reload a module: this adds the module -name to the __main__ namespace. You use Control-F5 to run a script: -this runs the script *in* the __main__ namespace. The latter also -sets sys.argv[] to the script name - -New in IDLE 0.4 (4/7/99) ------------------------- - -Most important change: a new menu entry "File -> Path browser", shows -a 4-column hierarchical browser which lets you browse sys.path, -directories, modules, and classes. Yes, it's a superset of the Class -browser menu entry. There's also a new internal module, -MultiScrolledLists.py, which provides the framework for this dialog. - -New in IDLE 0.3 (2/17/99) -------------------------- - -Most important changes: - -- Enabled support for running a module, with or without the debugger. -Output goes to a new window. Pressing F5 in a module is effectively a -reload of that module; Control-F5 loads it under the debugger. - -- Re-enable tearing off the Windows menu, and make a torn-off Windows -menu update itself whenever a window is opened or closed. - -- Menu items can now be have a checkbox (when the menu label starts -with "!"); use this for the Debugger and "Auto-open stack viewer" -(was: JIT stack viewer) menu items. - -- Added a Quit button to the Debugger API. - -- The current directory is explicitly inserted into sys.path. - -- Fix the debugger (when using Python 1.5.2b2) to use canonical -filenames for breakpoints, so these actually work. (There's still a -lot of work to be done to the management of breakpoints in the -debugger though.) - -- Closing a window that is still colorizing now actually works. - -- Allow dragging of the separator between the two list boxes in the -class browser. - -- Bind ESC to "close window" of the debugger, stack viewer and class -browser. It removes the selection highlighting in regular text -windows. (These are standard Windows conventions.) - ----------------------------------------------------------------------- - -New in IDLE 0.2 (1/8/99) ------------------------- - -Lots of changes; here are the highlights: - -General: - -- You can now write and configure your own IDLE extension modules; see -extend.txt. - - -File menu: - -The command to open the Python shell window is now in the File menu. - - -Edit menu: - -New Find dialog with more options; replace dialog; find in files dialog. - -Commands to tabify or untabify a region. - -Command to format a paragraph. - - -Debug menu: - -JIT (Just-In-Time) stack viewer toggle -- if set, the stack viewer -automaticall pops up when you get a traceback. - -Windows menu: - -Zoom height -- make the window full height. - - -Help menu: - -The help text now show up in a regular window so you can search and -even edit it if you like. - ----------------------------------------------------------------------- - -IDLE 0.1 was distributed with the Python 1.5.2b1 release on 12/22/98. - -====================================================================== diff --git a/Lib/idlelib/ObjectBrowser.py b/Lib/idlelib/ObjectBrowser.py deleted file mode 100644 index c235a7564f9a..000000000000 --- a/Lib/idlelib/ObjectBrowser.py +++ /dev/null @@ -1,151 +0,0 @@ -# XXX TO DO: -# - popup menu -# - support partial or total redisplay -# - more doc strings -# - tooltips - -# object browser - -# XXX TO DO: -# - for classes/modules, add "open source" to object browser - -from TreeWidget import TreeItem, TreeNode, ScrolledCanvas - -from repr import Repr - -myrepr = Repr() -myrepr.maxstring = 100 -myrepr.maxother = 100 - -class ObjectTreeItem(TreeItem): - def __init__(self, labeltext, object, setfunction=None): - self.labeltext = labeltext - self.object = object - self.setfunction = setfunction - def GetLabelText(self): - return self.labeltext - def GetText(self): - return myrepr.repr(self.object) - def GetIconName(self): - if not self.IsExpandable(): - return "python" - def IsEditable(self): - return self.setfunction is not None - def SetText(self, text): - try: - value = eval(text) - self.setfunction(value) - except: - pass - else: - self.object = value - def IsExpandable(self): - return not not dir(self.object) - def GetSubList(self): - keys = dir(self.object) - sublist = [] - for key in keys: - try: - value = getattr(self.object, key) - except AttributeError: - continue - item = make_objecttreeitem( - str(key) + " =", - value, - lambda value, key=key, object=self.object: - setattr(object, key, value)) - sublist.append(item) - return sublist - -class InstanceTreeItem(ObjectTreeItem): - def IsExpandable(self): - return 1 - def GetSubList(self): - sublist = ObjectTreeItem.GetSubList(self) - sublist.insert(0, - make_objecttreeitem("__class__ =", self.object.__class__)) - return sublist - -class ClassTreeItem(ObjectTreeItem): - def IsExpandable(self): - return 1 - def GetSubList(self): - sublist = ObjectTreeItem.GetSubList(self) - if len(self.object.__bases__) == 1: - item = make_objecttreeitem("__bases__[0] =", - self.object.__bases__[0]) - else: - item = make_objecttreeitem("__bases__ =", self.object.__bases__) - sublist.insert(0, item) - return sublist - -class AtomicObjectTreeItem(ObjectTreeItem): - def IsExpandable(self): - return 0 - -class SequenceTreeItem(ObjectTreeItem): - def IsExpandable(self): - return len(self.object) > 0 - def keys(self): - return range(len(self.object)) - def GetSubList(self): - sublist = [] - for key in self.keys(): - try: - value = self.object[key] - except KeyError: - continue - def setfunction(value, key=key, object=self.object): - object[key] = value - item = make_objecttreeitem(`key` + ":", value, setfunction) - sublist.append(item) - return sublist - -class DictTreeItem(SequenceTreeItem): - def keys(self): - keys = self.object.keys() - try: - keys.sort() - except: - pass - return keys - -from types import * - -dispatch = { - IntType: AtomicObjectTreeItem, - LongType: AtomicObjectTreeItem, - FloatType: AtomicObjectTreeItem, - StringType: AtomicObjectTreeItem, - TupleType: SequenceTreeItem, - ListType: SequenceTreeItem, - DictType: DictTreeItem, - InstanceType: InstanceTreeItem, - ClassType: ClassTreeItem, -} - -def make_objecttreeitem(labeltext, object, setfunction=None): - t = type(object) - if dispatch.has_key(t): - c = dispatch[t] - else: - c = ObjectTreeItem - return c(labeltext, object, setfunction) - -# Test script - -def _test(): - import sys - from Tkinter import Tk - root = Tk() - root.configure(bd=0, bg="yellow") - root.focus_set() - sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1) - sc.frame.pack(expand=1, fill="both") - item = make_objecttreeitem("sys", sys) - node = TreeNode(sc.canvas, None, item) - node.update() - root.mainloop() - -if __name__ == '__main__': - _test() diff --git a/Lib/idlelib/OldStackViewer.py b/Lib/idlelib/OldStackViewer.py deleted file mode 100644 index 2fa41275a1fc..000000000000 --- a/Lib/idlelib/OldStackViewer.py +++ /dev/null @@ -1,276 +0,0 @@ -import string -import sys -import os -from Tkinter import * -import linecache -from repr import Repr -from WindowList import ListedToplevel - -from ScrolledList import ScrolledList - - -class StackBrowser: - - def __init__(self, root, flist, stack=None): - self.top = top = ListedToplevel(root) - top.protocol("WM_DELETE_WINDOW", self.close) - top.bind("", self.close) - top.wm_title("Stack viewer") - top.wm_iconname("Stack") - # Create help label - self.helplabel = Label(top, - text="Click once to view variables; twice for source", - borderwidth=2, relief="groove") - self.helplabel.pack(fill="x") - # - self.sv = StackViewer(top, flist, self) - if stack is None: - stack = get_stack() - self.sv.load_stack(stack) - - def close(self, event=None): - self.top.destroy() - - localsframe = None - localsviewer = None - localsdict = None - globalsframe = None - globalsviewer = None - globalsdict = None - curframe = None - - def show_frame(self, (frame, lineno)): - if frame is self.curframe: - return - self.curframe = None - if frame.f_globals is not self.globalsdict: - self.show_globals(frame) - self.show_locals(frame) - self.curframe = frame - - def show_globals(self, frame): - title = "Global Variables" - if frame.f_globals.has_key("__name__"): - try: - name = str(frame.f_globals["__name__"]) + "" - except: - name = "" - if name: - title = title + " in module " + name - self.globalsdict = None - if self.globalsviewer: - self.globalsviewer.close() - self.globalsviewer = None - if not self.globalsframe: - self.globalsframe = Frame(self.top) - self.globalsdict = frame.f_globals - self.globalsviewer = NamespaceViewer( - self.globalsframe, - title, - self.globalsdict) - self.globalsframe.pack(fill="both", side="bottom") - - def show_locals(self, frame): - self.localsdict = None - if self.localsviewer: - self.localsviewer.close() - self.localsviewer = None - if frame.f_locals is not frame.f_globals: - title = "Local Variables" - code = frame.f_code - funcname = code.co_name - if funcname not in ("?", "", None): - title = title + " in " + funcname - if not self.localsframe: - self.localsframe = Frame(self.top) - self.localsdict = frame.f_locals - self.localsviewer = NamespaceViewer( - self.localsframe, - title, - self.localsdict) - self.localsframe.pack(fill="both", side="top") - else: - if self.localsframe: - self.localsframe.forget() - - -class StackViewer(ScrolledList): - - def __init__(self, master, flist, browser): - ScrolledList.__init__(self, master, width=80) - self.flist = flist - self.browser = browser - self.stack = [] - - def load_stack(self, stack, index=None): - self.stack = stack - self.clear() -## if len(stack) > 10: -## l["height"] = 10 -## self.topframe.pack(expand=1) -## else: -## l["height"] = len(stack) -## self.topframe.pack(expand=0) - for i in range(len(stack)): - frame, lineno = stack[i] - try: - modname = frame.f_globals["__name__"] - except: - modname = "?" - code = frame.f_code - filename = code.co_filename - funcname = code.co_name - sourceline = linecache.getline(filename, lineno) - sourceline = string.strip(sourceline) - if funcname in ("?", "", None): - item = "%s, line %d: %s" % (modname, lineno, sourceline) - else: - item = "%s.%s(), line %d: %s" % (modname, funcname, - lineno, sourceline) - if i == index: - item = "> " + item - self.append(item) - if index is not None: - self.select(index) - - def popup_event(self, event): - if self.stack: - return ScrolledList.popup_event(self, event) - - def fill_menu(self): - menu = self.menu - menu.add_command(label="Go to source line", - command=self.goto_source_line) - menu.add_command(label="Show stack frame", - command=self.show_stack_frame) - - def on_select(self, index): - if 0 <= index < len(self.stack): - self.browser.show_frame(self.stack[index]) - - def on_double(self, index): - self.show_source(index) - - def goto_source_line(self): - index = self.listbox.index("active") - self.show_source(index) - - def show_stack_frame(self): - index = self.listbox.index("active") - if 0 <= index < len(self.stack): - self.browser.show_frame(self.stack[index]) - - def show_source(self, index): - if not (0 <= index < len(self.stack)): - return - frame, lineno = self.stack[index] - code = frame.f_code - filename = code.co_filename - if os.path.isfile(filename): - edit = self.flist.open(filename) - if edit: - edit.gotoline(lineno) - - -def get_stack(t=None, f=None): - if t is None: - t = sys.last_traceback - stack = [] - if t and t.tb_frame is f: - t = t.tb_next - while f is not None: - stack.append((f, f.f_lineno)) - if f is self.botframe: - break - f = f.f_back - stack.reverse() - while t is not None: - stack.append((t.tb_frame, t.tb_lineno)) - t = t.tb_next - return stack - - -def getexception(type=None, value=None): - if type is None: - type = sys.last_type - value = sys.last_value - if hasattr(type, "__name__"): - type = type.__name__ - s = str(type) - if value is not None: - s = s + ": " + str(value) - return s - - -class NamespaceViewer: - - def __init__(self, master, title, dict=None): - width = 0 - height = 40 - if dict: - height = 20*len(dict) # XXX 20 == observed height of Entry widget - self.master = master - self.title = title - self.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.canvas = canvas = Canvas(frame, - height=min(300, max(40, height)), - scrollregion=(0, 0, width, height)) - 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.load_dict(dict) - - dict = -1 - - def load_dict(self, dict, force=0): - if dict is self.dict and not force: - return - subframe = self.subframe - frame = self.frame - for c in subframe.children.values(): - c.destroy() - self.dict = None - if not dict: - l = Label(subframe, text="None") - l.grid(row=0, column=0) - else: - names = dict.keys() - names.sort() - row = 0 - for name in names: - value = dict[name] - svalue = self.repr.repr(value) # repr(value) - l = Label(subframe, text=name) - l.grid(row=row, column=0, sticky="nw") - ## l = Label(subframe, text=svalue, justify="l", wraplength=300) - l = Entry(subframe, width=0, borderwidth=0) - l.insert(0, svalue) - ## l["state"] = "disabled" - l.grid(row=row, column=1, sticky="nw") - row = row+1 - self.dict = dict - # XXX Could we use a callback for the following? - subframe.update_idletasks() # Alas! - width = subframe.winfo_reqwidth() - height = subframe.winfo_reqheight() - canvas = self.canvas - self.canvas["scrollregion"] = (0, 0, width, height) - if height > 300: - canvas["height"] = 300 - frame.pack(expand=1) - else: - canvas["height"] = height - frame.pack(expand=0) - - def close(self): - self.frame.destroy() diff --git a/Lib/idlelib/OutputWindow.py b/Lib/idlelib/OutputWindow.py deleted file mode 100644 index 12280ad4d70f..000000000000 --- a/Lib/idlelib/OutputWindow.py +++ /dev/null @@ -1,279 +0,0 @@ -# changes by dscherer@cmu.edu -# - OutputWindow and OnDemandOutputWindow have been hastily -# extended to provide readline() support, an "iomark" separate -# from the "insert" cursor, and scrolling to clear the window. -# These changes are used by the ExecBinding module to provide -# standard input and output for user programs. Many of the new -# features are very similar to features of PyShell, which is a -# subclass of OutputWindow. Someone should make some sense of -# this. - -from Tkinter import * -from EditorWindow import EditorWindow -import re -import tkMessageBox - -from UndoDelegator import UndoDelegator - -class OutputUndoDelegator(UndoDelegator): - reading = 0 - # Forbid insert/delete before the I/O mark, in the blank lines after - # the output, or *anywhere* if we are not presently doing user input - def insert(self, index, chars, tags=None): - try: - if (self.delegate.compare(index, "<", "iomark") or - self.delegate.compare(index, ">", "endmark") or - (index!="iomark" and not self.reading)): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.insert(self, index, chars, tags) - def delete(self, index1, index2=None): - try: - if (self.delegate.compare(index1, "<", "iomark") or - self.delegate.compare(index1, ">", "endmark") or - (index2 and self.delegate.compare(index2, ">=", "endmark")) or - not self.reading): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.delete(self, index1, index2) - -class OutputWindow(EditorWindow): - """An editor window that can serve as an input and output file. - The input support has been rather hastily hacked in, and should - not be trusted. - """ - - UndoDelegator = OutputUndoDelegator - source_window = None - - def __init__(self, *args, **keywords): - if keywords.has_key('source_window'): - self.source_window = keywords['source_window'] - apply(EditorWindow.__init__, (self,) + args) - self.text.bind("<>", self.goto_file_line) - self.text.bind("<>", self.enter_callback) - self.text.mark_set("iomark","1.0") - self.text.mark_gravity("iomark", LEFT) - self.text.mark_set("endmark","1.0") - - # Customize EditorWindow - - def ispythonsource(self, filename): - # No colorization needed - return 0 - - def short_title(self): - return "Output" - - def long_title(self): - return "" - - def maybesave(self): - # Override base class method -- don't ask any questions - if self.get_saved(): - return "yes" - else: - return "no" - - # Act as input file - incomplete - - def set_line_and_column(self, event=None): - index = self.text.index(INSERT) - if (self.text.compare(index, ">", "endmark")): - self.text.mark_set("insert", "endmark") - self.text.see("insert") - EditorWindow.set_line_and_column(self) - - reading = 0 - canceled = 0 - endoffile = 0 - - def readline(self): - save = self.reading - try: - self.reading = self.undo.reading = 1 - self.text.mark_set("insert", "iomark") - self.text.see("insert") - self.top.mainloop() - finally: - self.reading = self.undo.reading = save - line = self.text.get("input", "iomark") - if self.canceled: - self.canceled = 0 - raise KeyboardInterrupt - if self.endoffile: - self.endoffile = 0 - return "" - return line or '\n' - - def close(self): - self.interrupt() - return EditorWindow.close(self) - - def interrupt(self): - if self.reading: - self.endoffile = 1 - self.top.quit() - - def enter_callback(self, event): - if self.reading and self.text.compare("insert", ">=", "iomark"): - self.text.mark_set("input", "iomark") - self.text.mark_set("iomark", "insert") - self.write('\n',"iomark") - self.text.tag_add("stdin", "input", "iomark") - self.text.update_idletasks() - self.top.quit() # Break out of recursive mainloop() in raw_input() - - return "break" - - # Act as output file - - def write(self, s, tags=(), mark="iomark"): - self.text.mark_gravity(mark, RIGHT) - self.text.insert(mark, str(s), tags) - self.text.mark_gravity(mark, LEFT) - self.text.see(mark) - self.text.update() - - def writelines(self, l): - map(self.write, l) - - def flush(self): - pass - - # Our own right-button menu - - rmenu_specs = [ - ("Go to file/line", "<>"), - ] - - file_line_pats = [ - r'file "([^"]*)", line (\d+)', - r'([^\s]+)\((\d+)\)', - r'([^\s]+):\s*(\d+):', - ] - - file_line_progs = None - - def goto_file_line(self, event=None): - 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") - 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") - 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) - return - filename, lineno = result - edit = self.untitled(filename) or self.flist.open(filename) - edit.gotoline(lineno) - edit.wakeup() - - def untitled(self, filename): - if filename!='Untitled' or not self.source_window or self.source_window.io.filename: - return None - return self.source_window - - def _file_line_helper(self, line): - for prog in self.file_line_progs: - m = prog.search(line) - if m: - break - else: - return None - filename, lineno = m.group(1, 2) - if not self.untitled(filename): - try: - f = open(filename, "r") - f.close() - except IOError: - return None - try: - return filename, int(lineno) - except TypeError: - return None - -# This classes now used by ExecBinding.py: - -class OnDemandOutputWindow: - source_window = None - - tagdefs = { - # XXX Should use IdlePrefs.ColorPrefs - "stdin": {"foreground": "black"}, - "stdout": {"foreground": "blue"}, - "stderr": {"foreground": "red"}, - } - - def __init__(self, flist): - self.flist = flist - self.owin = None - self.title = "Output" - self.close_hook = None - self.old_close = None - - def owclose(self): - if self.close_hook: - self.close_hook() - if self.old_close: - self.old_close() - - def set_title(self, title): - self.title = title - if self.owin and self.owin.text: - self.owin.saved_change_hook() - - def write(self, s, tags=(), mark="iomark"): - if not self.owin or not self.owin.text: - self.setup() - self.owin.write(s, tags, mark) - - def readline(self): - if not self.owin or not self.owin.text: - self.setup() - return self.owin.readline() - - def scroll_clear(self): - if self.owin and self.owin.text: - lineno = self.owin.getlineno("endmark") - self.owin.text.mark_set("insert","endmark") - self.owin.text.yview(float(lineno)) - self.owin.wakeup() - - def setup(self): - self.owin = owin = OutputWindow(self.flist, source_window = self.source_window) - owin.short_title = lambda self=self: self.title - text = owin.text - - self.old_close = owin.close_hook - owin.close_hook = self.owclose - - # xxx Bad hack: 50 blank lines at the bottom so that - # we can scroll the top of the window to the output - # cursor in scroll_clear(). There must be a better way... - owin.text.mark_gravity('endmark', LEFT) - owin.text.insert('iomark', '\n'*50) - owin.text.mark_gravity('endmark', RIGHT) - - for tag, cnf in self.tagdefs.items(): - if cnf: - apply(text.tag_configure, (tag,), cnf) - text.tag_raise('sel') diff --git a/Lib/idlelib/ParenMatch.py b/Lib/idlelib/ParenMatch.py deleted file mode 100644 index 17d76c2659c5..000000000000 --- a/Lib/idlelib/ParenMatch.py +++ /dev/null @@ -1,191 +0,0 @@ -"""ParenMatch -- An IDLE extension for parenthesis matching. - -When you hit a right paren, the cursor should move briefly to the left -paren. Paren here is used generically; the matching applies to -parentheses, square brackets, and curly braces. - -WARNING: This extension will fight with the CallTips extension, -because they both are interested in the KeyRelease-parenright event. -We'll have to fix IDLE to do something reasonable when two or more -extensions what to capture the same event. -""" - -import string - -import PyParse -from AutoIndent import AutoIndent, index2line -from IdleConf import idleconf - -class ParenMatch: - """Highlight matching parentheses - - There are three supported style of paren matching, based loosely - on the Emacs options. The style is select based on the - HILITE_STYLE attribute; it can be changed used the set_style - method. - - The supported styles are: - - default -- When a right paren is typed, highlight the matching - left paren for 1/2 sec. - - expression -- When a right paren is typed, highlight the entire - expression from the left paren to the right paren. - - TODO: - - fix interaction with CallTips - - extend IDLE with configuration dialog to change options - - implement rest of Emacs highlight styles (see below) - - print mismatch warning in IDLE status window - - Note: In Emacs, there are several styles of highlight where the - matching paren is highlighted whenever the cursor is immediately - to the right of a right paren. I don't know how to do that in Tk, - so I haven't bothered. - """ - - menudefs = [] - - keydefs = { - '<>' : ('', - '', - ''), - '<>' : ('',), - } - - windows_keydefs = {} - unix_keydefs = {} - - iconf = idleconf.getsection('ParenMatch') - STYLE = iconf.getdef('style', 'default') - FLASH_DELAY = iconf.getint('flash-delay') - HILITE_CONFIG = iconf.getcolor('hilite') - BELL = iconf.getboolean('bell') - del iconf - - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text - self.finder = LastOpenBracketFinder(editwin) - self.counter = 0 - self._restore = None - self.set_style(self.STYLE) - - def set_style(self, style): - self.STYLE = style - if style == "default": - self.create_tag = self.create_tag_default - self.set_timeout = self.set_timeout_last - elif style == "expression": - self.create_tag = self.create_tag_expression - self.set_timeout = self.set_timeout_none - - def flash_open_paren_event(self, event): - index = self.finder.find(keysym_type(event.keysym)) - if index is None: - self.warn_mismatched() - return - self._restore = 1 - self.create_tag(index) - self.set_timeout() - - def check_restore_event(self, event=None): - if self._restore: - self.text.tag_delete("paren") - self._restore = None - - def handle_restore_timer(self, timer_count): - if timer_count + 1 == self.counter: - self.check_restore_event() - - def warn_mismatched(self): - if self.BELL: - self.text.bell() - - # any one of the create_tag_XXX methods can be used depending on - # the style - - def create_tag_default(self, index): - """Highlight the single paren that matches""" - self.text.tag_add("paren", index) - self.text.tag_config("paren", self.HILITE_CONFIG) - - def create_tag_expression(self, index): - """Highlight the entire expression""" - self.text.tag_add("paren", index, "insert") - self.text.tag_config("paren", self.HILITE_CONFIG) - - # any one of the set_timeout_XXX methods can be used depending on - # the style - - def set_timeout_none(self): - """Highlight will remain until user input turns it off""" - pass - - 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.editwin.text_frame.after(self.FLASH_DELAY, - lambda self=self, c=self.counter: \ - self.handle_restore_timer(c)) - self.counter = self.counter + 1 - -def keysym_type(ks): - # Not all possible chars or keysyms are checked because of the - # limited context in which the function is used. - if ks == "parenright" or ks == "(": - return "paren" - if ks == "bracketright" or ks == "[": - return "bracket" - if ks == "braceright" or ks == "{": - return "brace" - -class LastOpenBracketFinder: - num_context_lines = AutoIndent.num_context_lines - indentwidth = AutoIndent.indentwidth - tabwidth = AutoIndent.tabwidth - context_use_ps1 = AutoIndent.context_use_ps1 - - def __init__(self, editwin): - self.editwin = editwin - self.text = editwin.text - - def _find_offset_in_buf(self, lno): - y = PyParse.Parser(self.indentwidth, self.tabwidth) - for context in self.num_context_lines: - startat = max(lno - context, 1) - startatindex = `startat` + ".0" - # rawtext needs to contain everything up to the last - # character, which was the close paren. the parser also - # requires that the last line ends with "\n" - rawtext = self.text.get(startatindex, "insert")[:-1] + "\n" - 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) - i = y.get_last_open_bracket_pos() - return i, y.str - - def find(self, right_keysym_type): - """Return the location of the last open paren""" - lno = index2line(self.text.index("insert")) - i, buf = self._find_offset_in_buf(lno) - if i is None \ - or keysym_type(buf[i]) != right_keysym_type: - return None - lines_back = string.count(buf[i:], "\n") - 1 - # subtract one for the "\n" added to please the parser - upto_open = buf[:i] - j = string.rfind(upto_open, "\n") + 1 # offset of column 0 of line - offset = i - j - return "%d.%d" % (lno - lines_back, offset) - - def _build_char_in_string_func(self, startindex): - def inner(offset, startindex=startindex, - icis=self.editwin.is_char_in_string): - return icis(startindex + "%dc" % offset) - return inner diff --git a/Lib/idlelib/PathBrowser.py b/Lib/idlelib/PathBrowser.py deleted file mode 100644 index 86cd2707dc11..000000000000 --- a/Lib/idlelib/PathBrowser.py +++ /dev/null @@ -1,95 +0,0 @@ -import os -import sys -import imp - -from TreeWidget import TreeItem -from ClassBrowser import ClassBrowser, ModuleBrowserTreeItem - -class PathBrowser(ClassBrowser): - - def __init__(self, flist): - self.init(flist) - - def settitle(self): - self.top.wm_title("Path Browser") - self.top.wm_iconname("Path Browser") - - def rootnode(self): - return PathBrowserTreeItem() - -class PathBrowserTreeItem(TreeItem): - - def GetText(self): - return "sys.path" - - def GetSubList(self): - sublist = [] - for dir in sys.path: - item = DirBrowserTreeItem(dir) - sublist.append(item) - return sublist - -class DirBrowserTreeItem(TreeItem): - - def __init__(self, dir, packages=[]): - self.dir = dir - self.packages = packages - - def GetText(self): - if not self.packages: - return self.dir - else: - return self.packages[-1] + ": package" - - def GetSubList(self): - try: - names = os.listdir(self.dir or os.curdir) - except os.error: - return [] - packages = [] - for name in names: - file = os.path.join(self.dir, name) - if self.ispackagedir(file): - nn = os.path.normcase(name) - packages.append((nn, name, file)) - packages.sort() - sublist = [] - for nn, name, file in packages: - item = DirBrowserTreeItem(file, self.packages + [name]) - sublist.append(item) - for nn, name in self.listmodules(names): - item = ModuleBrowserTreeItem(os.path.join(self.dir, name)) - sublist.append(item) - return sublist - - def ispackagedir(self, file): - if not os.path.isdir(file): - return 0 - init = os.path.join(file, "__init__.py") - return os.path.exists(init) - - def listmodules(self, allnames): - modules = {} - suffixes = imp.get_suffixes() - sorted = [] - for suff, mode, flag in suffixes: - i = -len(suff) - for name in allnames[:]: - normed_name = os.path.normcase(name) - if normed_name[i:] == suff: - mod_name = name[:i] - if not modules.has_key(mod_name): - modules[mod_name] = None - sorted.append((normed_name, name)) - allnames.remove(name) - sorted.sort() - return sorted - -def main(): - import PyShell - PathBrowser(PyShell.flist) - if sys.stdin is sys.__stdin__: - mainloop() - -if __name__ == "__main__": - main() diff --git a/Lib/idlelib/Percolator.py b/Lib/idlelib/Percolator.py deleted file mode 100644 index 5682111137b8..000000000000 --- a/Lib/idlelib/Percolator.py +++ /dev/null @@ -1,85 +0,0 @@ -from WidgetRedirector import WidgetRedirector -from Delegator import Delegator - -class Percolator: - - def __init__(self, text): - # XXX would be nice to inherit from Delegator - self.text = text - self.redir = WidgetRedirector(text) - self.top = self.bottom = Delegator(text) - self.bottom.insert = self.redir.register("insert", self.insert) - self.bottom.delete = self.redir.register("delete", self.delete) - self.filters = [] - - def close(self): - while self.top is not self.bottom: - self.removefilter(self.top) - self.top = None - self.bottom.setdelegate(None); self.bottom = None - self.redir.close(); self.redir = None - self.text = None - - def insert(self, index, chars, tags=None): - # Could go away if inheriting from Delegator - self.top.insert(index, chars, tags) - - def delete(self, index1, index2=None): - # Could go away if inheriting from Delegator - self.top.delete(index1, index2) - - def insertfilter(self, filter): - # Perhaps rename to pushfilter()? - assert isinstance(filter, Delegator) - assert filter.delegate is None - filter.setdelegate(self.top) - self.top = filter - - def removefilter(self, filter): - # XXX Perhaps should only support popfilter()? - assert isinstance(filter, Delegator) - assert filter.delegate is not None - f = self.top - if f is filter: - self.top = filter.delegate - filter.setdelegate(None) - else: - while f.delegate is not filter: - assert f is not self.bottom - f.resetcache() - f = f.delegate - f.setdelegate(filter.delegate) - filter.setdelegate(None) - - -def main(): - class Tracer(Delegator): - def __init__(self, name): - self.name = name - Delegator.__init__(self, None) - def insert(self, *args): - print self.name, ": insert", args - apply(self.delegate.insert, args) - def delete(self, *args): - print self.name, ": delete", args - apply(self.delegate.delete, args) - root = Tk() - root.wm_protocol("WM_DELETE_WINDOW", root.quit) - text = Text() - text.pack() - text.focus_set() - p = Percolator(text) - t1 = Tracer("t1") - t2 = Tracer("t2") - p.insertfilter(t1) - p.insertfilter(t2) - root.mainloop() - p.removefilter(t2) - root.mainloop() - p.insertfilter(t2) - p.removefilter(t1) - root.mainloop() - -if __name__ == "__main__": - from Tkinter import * - main() diff --git a/Lib/idlelib/PyParse.py b/Lib/idlelib/PyParse.py deleted file mode 100644 index c8212b214369..000000000000 --- a/Lib/idlelib/PyParse.py +++ /dev/null @@ -1,589 +0,0 @@ -import string -import re -import sys - -# Reason last stmt is continued (or C_NONE if it's not). -C_NONE, C_BACKSLASH, C_STRING, C_BRACKET = range(4) - -if 0: # for throwaway debugging output - def dump(*stuff): - sys.__stdout__.write(string.join(map(str, stuff), " ") + "\n") - -# Find what looks like the start of a popular stmt. - -_synchre = re.compile(r""" - ^ - [ \t]* - (?: if - | for - | while - | else - | def - | return - | assert - | break - | class - | continue - | elif - | try - | except - | raise - | import - | yield - ) - \b -""", re.VERBOSE | re.MULTILINE).search - -# Match blank line or non-indenting comment line. - -_junkre = re.compile(r""" - [ \t]* - (?: \# \S .* )? - \n -""", re.VERBOSE).match - -# Match any flavor of string; the terminating quote is optional -# so that we're robust in the face of incomplete program text. - -_match_stringre = re.compile(r""" - \""" [^"\\]* (?: - (?: \\. | "(?!"") ) - [^"\\]* - )* - (?: \""" )? - -| " [^"\\\n]* (?: \\. [^"\\\n]* )* "? - -| ''' [^'\\]* (?: - (?: \\. | '(?!'') ) - [^'\\]* - )* - (?: ''' )? - -| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '? -""", re.VERBOSE | re.DOTALL).match - -# Match a line that starts with something interesting; -# used to find the first item of a bracket structure. - -_itemre = re.compile(r""" - [ \t]* - [^\s#\\] # if we match, m.end()-1 is the interesting char -""", re.VERBOSE).match - -# Match start of stmts that should be followed by a dedent. - -_closere = re.compile(r""" - \s* - (?: return - | break - | continue - | raise - | pass - ) - \b -""", re.VERBOSE).match - -# Chew up non-special chars as quickly as possible. If match is -# successful, m.end() less 1 is the index of the last boring char -# matched. If match is unsuccessful, the string starts with an -# interesting char. - -_chew_ordinaryre = re.compile(r""" - [^[\](){}#'"\\]+ -""", re.VERBOSE).match - -# Build translation table to map uninteresting chars to "x", open -# brackets to "(", and close brackets to ")". - -_tran = ['x'] * 256 -for ch in "({[": - _tran[ord(ch)] = '(' -for ch in ")}]": - _tran[ord(ch)] = ')' -for ch in "\"'\\\n#": - _tran[ord(ch)] = ch -_tran = string.join(_tran, '') -del ch - -try: - UnicodeType = type(unicode("")) -except NameError: - UnicodeType = None - -class Parser: - - def __init__(self, indentwidth, tabwidth): - self.indentwidth = indentwidth - self.tabwidth = tabwidth - - def set_str(self, str): - assert len(str) == 0 or str[-1] == '\n' - if type(str) is UnicodeType: - # The parse functions have no idea what to do with Unicode, so - # replace all Unicode characters with "x". This is "safe" - # so long as the only characters germane to parsing the structure - # of Python are 7-bit ASCII. It's *necessary* because Unicode - # strings don't have a .translate() method that supports - # deletechars. - uniphooey = str - str = [] - push = str.append - for raw in map(ord, uniphooey): - push(raw < 127 and chr(raw) or "x") - str = "".join(str) - self.str = str - self.study_level = 0 - - # Return index of a good place to begin parsing, as close to the - # end of the string as possible. This will be the start of some - # popular stmt like "if" or "def". Return None if none found: - # the caller should pass more prior context then, if possible, or - # if not (the entire program text up until the point of interest - # has already been tried) pass 0 to set_lo. - # - # This will be reliable iff given a reliable is_char_in_string - # function, meaning that when it says "no", it's absolutely - # guaranteed that the char is not in a string. - # - # Ack, hack: in the shell window this kills us, because there's - # no way to tell the differences between output, >>> etc and - # user input. Indeed, IDLE's first output line makes the rest - # look like it's in an unclosed paren!: - # Python 1.5.2 (#0, Apr 13 1999, ... - - def find_good_parse_start(self, use_ps1, is_char_in_string=None, - _rfind=string.rfind, - _synchre=_synchre): - str, pos = self.str, None - if use_ps1: - # shell window - ps1 = '\n' + sys.ps1 - i = _rfind(str, ps1) - if i >= 0: - pos = i + len(ps1) - # make it look like there's a newline instead - # of ps1 at the start -- hacking here once avoids - # repeated hackery later - self.str = str[:pos-1] + '\n' + str[pos:] - return pos - - # File window -- real work. - if not is_char_in_string: - # no clue -- make the caller pass everything - return None - - # Peek back from the end for a good place to start, - # but don't try too often; pos will be left None, or - # bumped to a legitimate synch point. - limit = len(str) - for tries in range(5): - i = _rfind(str, ":\n", 0, limit) - if i < 0: - break - i = _rfind(str, '\n', 0, i) + 1 # start of colon line - m = _synchre(str, i, limit) - if m and not is_char_in_string(m.start()): - pos = m.start() - break - limit = i - if pos is None: - # Nothing looks like a block-opener, or stuff does - # but is_char_in_string keeps returning true; most likely - # we're in or near a giant string, the colorizer hasn't - # caught up enough to be helpful, or there simply *aren't* - # any interesting stmts. In any of these cases we're - # going to have to parse the whole thing to be sure, so - # give it one last try from the start, but stop wasting - # time here regardless of the outcome. - m = _synchre(str) - if m and not is_char_in_string(m.start()): - pos = m.start() - return pos - - # Peeking back worked; look forward until _synchre no longer - # matches. - i = pos + 1 - while 1: - m = _synchre(str, i) - if m: - s, i = m.span() - if not is_char_in_string(s): - pos = s - else: - break - return pos - - # Throw away the start of the string. Intended to be called with - # find_good_parse_start's result. - - def set_lo(self, lo): - assert lo == 0 or self.str[lo-1] == '\n' - if lo > 0: - self.str = self.str[lo:] - - # As quickly as humanly possible , find the line numbers (0- - # based) of the non-continuation lines. - # Creates self.{goodlines, continuation}. - - def _study1(self, _replace=string.replace, _find=string.find): - if self.study_level >= 1: - return - self.study_level = 1 - - # Map all uninteresting characters to "x", all open brackets - # to "(", all close brackets to ")", then collapse runs of - # uninteresting characters. This can cut the number of chars - # by a factor of 10-40, and so greatly speed the following loop. - str = self.str - str = string.translate(str, _tran) - str = _replace(str, 'xxxxxxxx', 'x') - str = _replace(str, 'xxxx', 'x') - str = _replace(str, 'xx', 'x') - str = _replace(str, 'xx', 'x') - str = _replace(str, '\nx', '\n') - # note that replacing x\n with \n would be incorrect, because - # x may be preceded by a backslash - - # March over the squashed version of the program, accumulating - # the line numbers of non-continued stmts, and determining - # whether & why the last stmt is a continuation. - continuation = C_NONE - level = lno = 0 # level is nesting level; lno is line number - self.goodlines = goodlines = [0] - push_good = goodlines.append - i, n = 0, len(str) - while i < n: - ch = str[i] - i = i+1 - - # cases are checked in decreasing order of frequency - if ch == 'x': - continue - - if ch == '\n': - lno = lno + 1 - if level == 0: - push_good(lno) - # else we're in an unclosed bracket structure - continue - - if ch == '(': - level = level + 1 - continue - - if ch == ')': - if level: - level = level - 1 - # else the program is invalid, but we can't complain - continue - - if ch == '"' or ch == "'": - # consume the string - quote = ch - if str[i-1:i+2] == quote * 3: - quote = quote * 3 - w = len(quote) - 1 - i = i+w - while i < n: - ch = str[i] - i = i+1 - - if ch == 'x': - continue - - if str[i-1:i+w] == quote: - i = i+w - break - - if ch == '\n': - lno = lno + 1 - if w == 0: - # unterminated single-quoted string - if level == 0: - push_good(lno) - break - continue - - if ch == '\\': - assert i < n - if str[i] == '\n': - lno = lno + 1 - i = i+1 - continue - - # else comment char or paren inside string - - else: - # didn't break out of the loop, so we're still - # inside a string - continuation = C_STRING - continue # with outer loop - - if ch == '#': - # consume the comment - i = _find(str, '\n', i) - assert i >= 0 - continue - - assert ch == '\\' - assert i < n - if str[i] == '\n': - lno = lno + 1 - if i+1 == n: - continuation = C_BACKSLASH - i = i+1 - - # The last stmt may be continued for all 3 reasons. - # String continuation takes precedence over bracket - # continuation, which beats backslash continuation. - if continuation != C_STRING and level > 0: - continuation = C_BRACKET - self.continuation = continuation - - # Push the final line number as a sentinel value, regardless of - # whether it's continued. - assert (continuation == C_NONE) == (goodlines[-1] == lno) - if goodlines[-1] != lno: - push_good(lno) - - def get_continuation_type(self): - self._study1() - return self.continuation - - # study1 was sufficient to determine the continuation status, - # but doing more requires looking at every character. study2 - # does this for the last interesting statement in the block. - # Creates: - # self.stmt_start, stmt_end - # slice indices of last interesting stmt - # self.lastch - # last non-whitespace character before optional trailing - # comment - # self.lastopenbracketpos - # if continuation is C_BRACKET, index of last open bracket - - def _study2(self, _rfind=string.rfind, _find=string.find, - _ws=string.whitespace): - if self.study_level >= 2: - return - self._study1() - self.study_level = 2 - - # Set p and q to slice indices of last interesting stmt. - str, goodlines = self.str, self.goodlines - i = len(goodlines) - 1 - p = len(str) # index of newest line - while i: - assert p - # p is the index of the stmt at line number goodlines[i]. - # Move p back to the stmt at line number goodlines[i-1]. - q = p - for nothing in range(goodlines[i-1], goodlines[i]): - # tricky: sets p to 0 if no preceding newline - p = _rfind(str, '\n', 0, p-1) + 1 - # The stmt str[p:q] isn't a continuation, but may be blank - # or a non-indenting comment line. - if _junkre(str, p): - i = i-1 - else: - break - if i == 0: - # nothing but junk! - assert p == 0 - q = p - self.stmt_start, self.stmt_end = p, q - - # Analyze this stmt, to find the last open bracket (if any) - # and last interesting character (if any). - lastch = "" - stack = [] # stack of open bracket indices - push_stack = stack.append - while p < q: - # suck up all except ()[]{}'"#\\ - m = _chew_ordinaryre(str, p, q) - if m: - # we skipped at least one boring char - newp = m.end() - # back up over totally boring whitespace - i = newp - 1 # index of last boring char - while i >= p and str[i] in " \t\n": - i = i-1 - if i >= p: - lastch = str[i] - p = newp - if p >= q: - break - - ch = str[p] - - if ch in "([{": - push_stack(p) - lastch = ch - p = p+1 - continue - - if ch in ")]}": - if stack: - del stack[-1] - lastch = ch - p = p+1 - continue - - if ch == '"' or ch == "'": - # consume string - # Note that study1 did this with a Python loop, but - # we use a regexp here; the reason is speed in both - # cases; the string may be huge, but study1 pre-squashed - # strings to a couple of characters per line. study1 - # also needed to keep track of newlines, and we don't - # have to. - lastch = ch - p = _match_stringre(str, p, q).end() - continue - - if ch == '#': - # consume comment and trailing newline - p = _find(str, '\n', p, q) + 1 - assert p > 0 - continue - - assert ch == '\\' - p = p+1 # beyond backslash - assert p < q - if str[p] != '\n': - # the program is invalid, but can't complain - lastch = ch + str[p] - p = p+1 # beyond escaped char - - # end while p < q: - - self.lastch = lastch - if stack: - self.lastopenbracketpos = stack[-1] - - # Assuming continuation is C_BRACKET, return the number - # of spaces the next line should be indented. - - def compute_bracket_indent(self, _find=string.find): - self._study2() - assert self.continuation == C_BRACKET - j = self.lastopenbracketpos - str = self.str - n = len(str) - origi = i = string.rfind(str, '\n', 0, j) + 1 - j = j+1 # one beyond open bracket - # find first list item; set i to start of its line - while j < n: - m = _itemre(str, j) - if m: - j = m.end() - 1 # index of first interesting char - extra = 0 - break - else: - # this line is junk; advance to next line - i = j = _find(str, '\n', j) + 1 - else: - # nothing interesting follows the bracket; - # reproduce the bracket line's indentation + a level - j = i = origi - while str[j] in " \t": - j = j+1 - extra = self.indentwidth - return len(string.expandtabs(str[i:j], - self.tabwidth)) + extra - - # Return number of physical lines in last stmt (whether or not - # it's an interesting stmt! this is intended to be called when - # continuation is C_BACKSLASH). - - def get_num_lines_in_stmt(self): - self._study1() - goodlines = self.goodlines - return goodlines[-1] - goodlines[-2] - - # Assuming continuation is C_BACKSLASH, return the number of spaces - # the next line should be indented. Also assuming the new line is - # the first one following the initial line of the stmt. - - def compute_backslash_indent(self): - self._study2() - assert self.continuation == C_BACKSLASH - str = self.str - i = self.stmt_start - while str[i] in " \t": - i = i+1 - startpos = i - - # See whether the initial line starts an assignment stmt; i.e., - # look for an = operator - endpos = string.find(str, '\n', startpos) + 1 - found = level = 0 - while i < endpos: - ch = str[i] - if ch in "([{": - level = level + 1 - i = i+1 - elif ch in ")]}": - if level: - level = level - 1 - i = i+1 - elif ch == '"' or ch == "'": - i = _match_stringre(str, i, endpos).end() - elif ch == '#': - break - elif level == 0 and ch == '=' and \ - (i == 0 or str[i-1] not in "=<>!") and \ - str[i+1] != '=': - found = 1 - break - else: - i = i+1 - - if found: - # found a legit =, but it may be the last interesting - # thing on the line - i = i+1 # move beyond the = - found = re.match(r"\s*\\", str[i:endpos]) is None - - if not found: - # oh well ... settle for moving beyond the first chunk - # of non-whitespace chars - i = startpos - while str[i] not in " \t\n": - i = i+1 - - return len(string.expandtabs(str[self.stmt_start : - i], - self.tabwidth)) + 1 - - # Return the leading whitespace on the initial line of the last - # interesting stmt. - - def get_base_indent_string(self): - self._study2() - i, n = self.stmt_start, self.stmt_end - j = i - str = self.str - while j < n and str[j] in " \t": - j = j + 1 - return str[i:j] - - # Did the last interesting stmt open a block? - - def is_block_opener(self): - self._study2() - return self.lastch == ':' - - # Did the last interesting stmt close a block? - - def is_block_closer(self): - self._study2() - return _closere(self.str, self.stmt_start) is not None - - # index of last open bracket ({[, or None if none - lastopenbracketpos = None - - def get_last_open_bracket_pos(self): - self._study2() - return self.lastopenbracketpos diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py deleted file mode 100644 index 503780684d5e..000000000000 --- a/Lib/idlelib/PyShell.py +++ /dev/null @@ -1,903 +0,0 @@ -#! /usr/bin/env python - -# changes by dscherer@cmu.edu - -# The main() function has been replaced by a whole class, in order to -# address the constraint that only one process can sit on the port -# hard-coded into the loader. - -# It attempts to load the RPC protocol server and publish itself. If -# that fails, it assumes that some other copy of IDLE is already running -# on the port and attempts to contact it. It then uses the RPC mechanism -# to ask that copy to do whatever it was instructed (via the command -# line) to do. (Think netscape -remote). The handling of command line -# arguments for remotes is still very incomplete. - -# Default behavior (no command line options) is to open an editor window -# instead of starting the Python Shell. However, if called as -# Pyshell.main(0), the Shell will be started instead of the editor window. - -# In the default editor mode, if files are specified, they are opened. - -# If any command line options are specified, a shell does appear, and if -# the -e option is used, both a shell and an editor window open. - -import os -import spawn -import sys -import string -import getopt -import re -import protocol -import warnings - -import linecache -from code import InteractiveInterpreter - -from Tkinter import * -import tkMessageBox - -from EditorWindow import EditorWindow, fixwordbreaks -from FileList import FileList -from ColorDelegator import ColorDelegator -from UndoDelegator import UndoDelegator -from OutputWindow import OutputWindow, OnDemandOutputWindow -from IdleConf import idleconf -from configHandler import idleConf -import idlever - -# We need to patch linecache.checkcache, because we don't want it -# to throw away our entries. -# Rather than repeating its code here, we save those entries, -# then call the original function, and then restore the saved entries. -def linecache_checkcache(orig_checkcache=linecache.checkcache): - cache = linecache.cache - save = {} - for filename in cache.keys(): - if filename[:1] + filename[-1:] == '<>': - save[filename] = cache[filename] - orig_checkcache() - cache.update(save) -linecache.checkcache = linecache_checkcache - - -# Note: <> event is defined in AutoIndent.py - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ win -#$ unix -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> - -#$ event <> - - -class PyShellEditorWindow(EditorWindow): - - # Regular text edit window when a shell is present - # XXX ought to merge with regular editor window - - def __init__(self, *args): - apply(EditorWindow.__init__, (self,) + args) - self.text.bind("<>", self.set_breakpoint_here) - self.text.bind("<>", self.flist.open_shell) - - rmenu_specs = [ - ("Set breakpoint here", "<>"), - ] - - def set_breakpoint_here(self, event=None): - if not self.flist.pyshell or not self.flist.pyshell.interp.debugger: - self.text.bell() - return - self.flist.pyshell.interp.debugger.set_breakpoint_here(self) - - -class PyShellFileList(FileList): - - # File list when a shell is present - - EditorWindow = PyShellEditorWindow - - pyshell = None - - def open_shell(self, event=None): - if self.pyshell: - self.pyshell.wakeup() - else: - self.pyshell = PyShell(self) - self.pyshell.begin() - return self.pyshell - - -class ModifiedColorDelegator(ColorDelegator): - - # Colorizer for the shell window itself - - def recolorize_main(self): - self.tag_remove("TODO", "1.0", "iomark") - self.tag_add("SYNC", "1.0", "iomark") - ColorDelegator.recolorize_main(self) - - tagdefs = ColorDelegator.tagdefs.copy() - theme = idleConf.GetOption('main','Theme','name') - tagdefs.update({ - - "stdin": idleConf.GetHighlight(theme, "stdin"), - "stdout": idleConf.GetHighlight(theme, "stdout"), - "stderr": idleConf.GetHighlight(theme, "stderr"), - "console": idleConf.GetHighlight(theme, "console"), - "ERROR": idleConf.GetHighlight(theme, "error"), - None: idleConf.GetHighlight(theme, "normal"), - }) - - -class ModifiedUndoDelegator(UndoDelegator): - - # Forbid insert/delete before the I/O mark - - def insert(self, index, chars, tags=None): - try: - if self.delegate.compare(index, "<", "iomark"): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.insert(self, index, chars, tags) - - def delete(self, index1, index2=None): - try: - if self.delegate.compare(index1, "<", "iomark"): - self.delegate.bell() - return - except TclError: - pass - UndoDelegator.delete(self, index1, index2) - -class ModifiedInterpreter(InteractiveInterpreter): - - def __init__(self, tkconsole): - self.tkconsole = tkconsole - locals = sys.modules['__main__'].__dict__ - InteractiveInterpreter.__init__(self, locals=locals) - self.save_warnings_filters = None - - gid = 0 - - def execsource(self, source): - # Like runsource() but assumes complete exec source - filename = self.stuffsource(source) - self.execfile(filename, source) - - def execfile(self, filename, source=None): - # Execute an existing file - if source is None: - source = open(filename, "r").read() - try: - code = compile(source, filename, "exec") - except (OverflowError, SyntaxError): - self.tkconsole.resetoutput() - InteractiveInterpreter.showsyntaxerror(self, filename) - else: - self.runcode(code) - - def runsource(self, source): - # Extend base class to stuff the source in the line cache first - filename = self.stuffsource(source) - self.more = 0 - self.save_warnings_filters = warnings.filters[:] - warnings.filterwarnings(action="error", category=SyntaxWarning) - try: - return InteractiveInterpreter.runsource(self, source, filename) - finally: - if self.save_warnings_filters is not None: - warnings.filters[:] = self.save_warnings_filters - self.save_warnings_filters = None - - def stuffsource(self, source): - # Stuff source in the filename cache - filename = "" % self.gid - self.gid = self.gid + 1 - lines = string.split(source, "\n") - linecache.cache[filename] = len(source)+1, 0, lines, filename - return filename - - def showsyntaxerror(self, filename=None): - # Extend base class to color the offending position - # (instead of printing it and pointing at it with a caret) - text = self.tkconsole.text - stuff = self.unpackerror() - if not stuff: - self.tkconsole.resetoutput() - InteractiveInterpreter.showsyntaxerror(self, filename) - return - msg, lineno, offset, line = stuff - if lineno == 1: - pos = "iomark + %d chars" % (offset-1) - else: - pos = "iomark linestart + %d lines + %d chars" % (lineno-1, - offset-1) - text.tag_add("ERROR", pos) - text.see(pos) - char = text.get(pos) - if char and char in string.letters + string.digits + "_": - text.tag_add("ERROR", pos + " wordstart", pos) - self.tkconsole.resetoutput() - self.write("SyntaxError: %s\n" % str(msg)) - - def unpackerror(self): - type, value, tb = sys.exc_info() - ok = type is SyntaxError - if ok: - try: - msg, (dummy_filename, lineno, offset, line) = value - except: - ok = 0 - if ok: - return msg, lineno, offset, line - else: - return None - - def showtraceback(self): - # Extend base class method to reset output properly - text = self.tkconsole.text - self.tkconsole.resetoutput() - self.checklinecache() - InteractiveInterpreter.showtraceback(self) - - def checklinecache(self): - c = linecache.cache - for key in c.keys(): - if key[:1] + key[-1:] != "<>": - del c[key] - - debugger = None - - def setdebugger(self, debugger): - self.debugger = debugger - - def getdebugger(self): - return self.debugger - - def runcode(self, code): - # Override base class method - if self.save_warnings_filters is not None: - warnings.filters[:] = self.save_warnings_filters - self.save_warnings_filters = None - debugger = self.debugger - try: - self.tkconsole.beginexecuting() - try: - if debugger: - debugger.run(code, self.locals) - else: - exec code in self.locals - except SystemExit: - if tkMessageBox.askyesno( - "Exit?", - "Do you want to exit altogether?", - default="yes", - master=self.tkconsole.text): - raise - else: - self.showtraceback() - if self.tkconsole.getvar("<>"): - self.tkconsole.open_stack_viewer() - except: - self.showtraceback() - if self.tkconsole.getvar("<>"): - self.tkconsole.open_stack_viewer() - - finally: - self.tkconsole.endexecuting() - - def write(self, s): - # Override base class write - self.tkconsole.console.write(s) - - -class PyShell(OutputWindow): - - shell_title = "Python Shell" - - # Override classes - ColorDelegator = ModifiedColorDelegator - UndoDelegator = ModifiedUndoDelegator - - # Override menu bar specs - menu_specs = PyShellEditorWindow.menu_specs[:] - menu_specs.insert(len(menu_specs)-3, ("debug", "_Debug")) - - # New classes - from IdleHistory import History - - def __init__(self, flist=None): - self.interp = ModifiedInterpreter(self) - if flist is None: - root = Tk() - fixwordbreaks(root) - root.withdraw() - flist = PyShellFileList(root) - - OutputWindow.__init__(self, flist, None, None) - - import __builtin__ - __builtin__.quit = __builtin__.exit = "To exit, type Ctrl-D." - - self.auto = self.extensions["AutoIndent"] # Required extension - self.auto.config(usetabs=1, indentwidth=8, context_use_ps1=1) - - 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.home_callback) - text.bind("<>", self.eof_callback) - text.bind("<>", self.open_stack_viewer) - text.bind("<>", self.toggle_debugger) - text.bind("<>", self.flist.open_shell) - text.bind("<>", self.toggle_jit_stack_viewer) - - self.save_stdout = sys.stdout - self.save_stderr = sys.stderr - self.save_stdin = sys.stdin - sys.stdout = PseudoFile(self, "stdout") - sys.stderr = PseudoFile(self, "stderr") - sys.stdin = self - self.console = PseudoFile(self, "console") - - self.history = self.History(self.text) - - reading = 0 - executing = 0 - canceled = 0 - endoffile = 0 - - 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) - self.set_debugger_indicator() - return "break" - else: - db = self.interp.getdebugger() - if db: - self.close_debugger() - else: - self.open_debugger() - - def set_debugger_indicator(self): - db = self.interp.getdebugger() - self.setvar("<>", not not db) - - def toggle_jit_stack_viewer( self, event=None): - pass # All we need is the variable - - def close_debugger(self): - db = self.interp.getdebugger() - if db: - self.interp.setdebugger(None) - db.close() - self.resetoutput() - self.console.write("[DEBUG OFF]\n") - sys.ps1 = ">>> " - self.showprompt() - self.set_debugger_indicator() - - def open_debugger(self): - import Debugger - self.interp.setdebugger(Debugger.Debugger(self)) - sys.ps1 = "[DEBUG ON]\n>>> " - self.showprompt() - self.set_debugger_indicator() - - def beginexecuting(self): - # Helper for ModifiedInterpreter - self.resetoutput() - self.executing = 1 - ##self._cancel_check = self.cancel_check - ##sys.settrace(self._cancel_check) - - def endexecuting(self): - # Helper for ModifiedInterpreter - ##sys.settrace(None) - ##self._cancel_check = None - self.executing = 0 - self.canceled = 0 - - def close(self): - # Extend base class method - if self.executing: - # XXX Need to ask a question here - if not tkMessageBox.askokcancel( - "Kill?", - "The program is still running; do you want to kill it?", - default="ok", - master=self.text): - return "cancel" - self.canceled = 1 - if self.reading: - self.top.quit() - return "cancel" - return OutputWindow.close(self) - - def _close(self): - self.close_debugger() - # Restore std streams - sys.stdout = self.save_stdout - sys.stderr = self.save_stderr - sys.stdin = self.save_stdin - # Break cycles - self.interp = None - self.console = None - self.auto = None - self.flist.pyshell = None - self.history = None - OutputWindow._close(self) # Really EditorWindow._close - - def ispythonsource(self, filename): - # Override this so EditorWindow never removes the colorizer - return 1 - - def short_title(self): - return self.shell_title - - COPYRIGHT = \ - 'Type "copyright", "credits" or "license" for more information.' - - def begin(self): - self.resetoutput() - self.write("Python %s on %s\n%s\nIDLE Fork %s -- press F1 for help\n" % - (sys.version, sys.platform, self.COPYRIGHT, - idlever.IDLE_VERSION)) - try: - sys.ps1 - except AttributeError: - sys.ps1 = ">>> " - self.showprompt() - import Tkinter - Tkinter._default_root = None - - def interact(self): - self.begin() - self.top.mainloop() - - def readline(self): - save = self.reading - try: - self.reading = 1 - self.top.mainloop() - finally: - self.reading = save - line = self.text.get("iomark", "end-1c") - self.resetoutput() - if self.canceled: - self.canceled = 0 - raise KeyboardInterrupt - if self.endoffile: - self.endoffile = 0 - return "" - return line - - def isatty(self): - return 1 - - def cancel_callback(self, event): - try: - if self.text.compare("sel.first", "!=", "sel.last"): - return # Active selection -- always use default binding - except: - pass - if not (self.executing or self.reading): - self.resetoutput() - self.write("KeyboardInterrupt\n") - self.showprompt() - return "break" - self.endoffile = 0 - self.canceled = 1 - if self.reading: - self.top.quit() - return "break" - - def eof_callback(self, event): - 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")): - return # Let the default binding (delete next char) take over - if not self.executing: -## if not tkMessageBox.askokcancel( -## "Exit?", -## "Are you sure you want to exit?", -## default="ok", master=self.text): -## return "break" - self.resetoutput() - self.close() - else: - self.canceled = 0 - self.endoffile = 1 - self.top.quit() - return "break" - - def home_callback(self, event): - if event.state != 0 and event.keysym == "Home": - return # ; fall back to class binding - if self.text.compare("iomark", "<=", "insert") and \ - self.text.compare("insert linestart", "<=", "iomark"): - self.text.mark_set("insert", "iomark") - self.text.tag_remove("sel", "1.0", "end") - self.text.see("insert") - return "break" - - def linefeed_callback(self, event): - # Insert a linefeed without entering anything (still autoindented) - if self.reading: - self.text.insert("insert", "\n") - self.text.see("insert") - else: - self.auto.auto_indent(event) - return "break" - - def enter_callback(self, event): - 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") - if sel: - if self.text.compare("sel.last", "<=", "iomark"): - self.recall(sel) - return "break" - except: - pass - # 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"): - # 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])) - 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])) - return "break" - # No stdin mark -- just get the current line - self.recall(self.text.get("insert linestart", "insert lineend")) - return "break" - # 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") - if s and not string.strip(s): - self.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"): - self.auto.auto_indent(event) - return "break" - # We're in the last line; append a newline and submit it - self.text.mark_set("insert", "end-1c") - if self.reading: - self.text.insert("insert", "\n") - self.text.see("insert") - else: - self.auto.auto_indent(event) - self.text.tag_add("stdin", "iomark", "end-1c") - self.text.update_idletasks() - if self.reading: - self.top.quit() # Break out of recursive mainloop() in raw_input() - else: - self.runit() - return "break" - - def recall(self, s): - if self.history: - self.history.recall(s) - - def runit(self): - line = self.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) - while i > 0 and line[i-1] in " \t": - i = i-1 - if i > 0 and line[i-1] == "\n": - i = i-1 - while i > 0 and line[i-1] in " \t": - i = i-1 - line = line[:i] - more = self.interp.runsource(line) - if not more: - self.showprompt() - - def cancel_check(self, frame, what, args, - dooneevent=tkinter.dooneevent, - dontwait=tkinter.DONT_WAIT): - # Hack -- use the debugger hooks to be able to handle events - # and interrupt execution at any time. - # This slows execution down quite a bit, so you may want to - # disable this (by not calling settrace() in runcode() above) - # for full-bore (uninterruptable) speed. - # XXX This should become a user option. - if self.canceled: - return - dooneevent(dontwait) - if self.canceled: - self.canceled = 0 - raise KeyboardInterrupt - return self._cancel_check - - def open_stack_viewer(self, event=None): - try: - sys.last_traceback - except: - tkMessageBox.showerror("No stack trace", - "There is no stack trace yet.\n" - "(sys.last_traceback is not defined)", - master=self.text) - return - from StackViewer import StackBrowser - sv = StackBrowser(self.root, self.flist) - - def showprompt(self): - self.resetoutput() - try: - s = str(sys.ps1) - except: - s = "" - self.console.write(s) - self.text.mark_set("insert", "end-1c") - - 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") - sys.stdout.softspace = 0 - - def write(self, s, tags=()): - self.text.mark_gravity("iomark", "right") - OutputWindow.write(self, s, tags, "iomark") - self.text.mark_gravity("iomark", "left") - if self.canceled: - self.canceled = 0 - raise KeyboardInterrupt - -class PseudoFile: - - def __init__(self, shell, tags): - self.shell = shell - self.tags = tags - - def write(self, s): - self.shell.write(s, self.tags) - - def writelines(self, l): - map(self.write, l) - - def flush(self): - pass - - def isatty(self): - return 1 - -usage_msg = """\ -usage: idle.py [-c command] [-d] [-i] [-r script] [-s] [-t title] [arg] ... - -idle file(s) (without options) edit the file(s) - --c cmd run the command in a shell --d enable the debugger --i open an interactive shell --i file(s) open a shell and also an editor window for each file --r script run a file as a script in a shell --s run $IDLESTARTUP or $PYTHONSTARTUP before anything else --t title set title of shell window - -Remaining arguments are applied to the command (-c) or script (-r). -""" - -class usageError: - def __init__(self, string): self.string = string - def __repr__(self): return self.string - -class main: - def __init__(self, noshell=1): - - global flist, root - root = Tk(className="Idle") - fixwordbreaks(root) - root.withdraw() - flist = PyShellFileList(root) - - dbg=OnDemandOutputWindow(flist) - dbg.set_title('IDLE Debugging Messages') - sys.stdout = PseudoFile(dbg,['stdout']) - sys.stderr = PseudoFile(dbg,['stderr']) - - try: - self.server = protocol.Server(connection_hook = self.address_ok) - protocol.publish( 'IDLE', self.connect ) - self.main(sys.argv[1:], noshell) - return - except protocol.connectionLost: - try: - client = protocol.Client() - IDLE = client.getobject('IDLE') - if IDLE: - try: - IDLE.remote( sys.argv[1:] ) - except usageError, msg: - sys.stderr.write("Error: %s\n" % str(msg)) - sys.stderr.write(usage_msg) - return - except protocol.connectionLost: - pass - - #maybe the following should be handled by a tkmessagebox for - #users who don't start idle from a console?? - print """\ -IDLE cannot run. - -IDLE needs to use a specific TCP/IP port (7454) in order to execute and -debug programs. IDLE is unable to bind to this port, and so cannot -start. Here are some possible causes of this problem: - - 1. TCP/IP networking is not installed or not working on this computer - 2. Another program is running that uses this port - 3. Another copy of IDLE stopped responding but is still bound to the port - 4. Personal firewall software is preventing IDLE from using this port - -IDLE makes and accepts connections only with this computer, and does not -communicate over the internet in any way. It's use of port 7454 should not -be a security risk on a single-user machine. -""" - dbg.owin.gotoline(1) - dbg.owin.remove_selection() - root.mainloop() # wait for user to read message - - def idle(self): - spawn.kill_zombies() - self.server.rpc_loop() - root.after(25, self.idle) - - # We permit connections from localhost only - def address_ok(self, addr): - return addr[0] == '127.0.0.1' - - def connect(self, client, addr): - return self - - def remote( self, argv ): - # xxx Should make this behavior match the behavior in main, or redo - # command line options entirely. - - try: - opts, args = getopt.getopt(argv, "c:deist:") - except getopt.error, msg: - raise usageError(msg) - - for filename in args: - flist.open(filename) - if not args: - flist.new() - - def main(self, argv, noshell): - cmd = None - edit = 0 - debug = 0 - interactive = 0 - script = None - startup = 0 - - try: - opts, args = getopt.getopt(argv, "c:dir:st:") - except getopt.error, msg: - sys.stderr.write("Error: %s\n" % str(msg)) - sys.stderr.write(usage_msg) - sys.exit(2) - - for o, a in opts: - noshell = 0 # There are options, bring up a shell - if o == '-c': - cmd = a - if o == '-d': - debug = 1 - if o == '-i': - interactive = 1 - if o == '-r': - script = a - if o == '-s': - startup = 1 - if o == '-t': - PyShell.shell_title = a - - if noshell: edit=1 - if interactive and args and args[0] != "-": edit = 1 - - for i in range(len(sys.path)): - sys.path[i] = os.path.abspath(sys.path[i]) - - pathx = [] - if edit: - for filename in args: - pathx.append(os.path.dirname(filename)) - elif args and args[0] != "-": - pathx.append(os.path.dirname(args[0])) - else: - pathx.append(os.curdir) - for dir in pathx: - dir = os.path.abspath(dir) - if not dir in sys.path: - sys.path.insert(0, dir) - - if edit: - for filename in args: - flist.open(filename) - if not args: - flist.new() - else: - if cmd: - sys.argv = ["-c"] + args - else: - sys.argv = args or [""] - - if noshell: - flist.pyshell = None - else: - shell = PyShell(flist) - interp = shell.interp - flist.pyshell = shell - - if startup: - filename = os.environ.get("IDLESTARTUP") or \ - os.environ.get("PYTHONSTARTUP") - if filename and os.path.isfile(filename): - interp.execfile(filename) - - if debug: - shell.open_debugger() - if cmd: - interp.execsource(cmd) - elif script: - if os.path.isfile(script): - interp.execfile(script) - else: - print "No script file: ", script - shell.begin() - - self.idle() - root.mainloop() - root.destroy() - - -if __name__ == "__main__": - main() diff --git a/Lib/idlelib/README.txt b/Lib/idlelib/README.txt deleted file mode 100644 index 152d497bea29..000000000000 --- a/Lib/idlelib/README.txt +++ /dev/null @@ -1,158 +0,0 @@ -IDLEfork README -=============== - -IDLEfork is an official experimental fork of Python's Integrated -DeveLopment Environment IDLE. Worthwhile and successful changes and -additions will go back into the Python distribution's IDLE at some -later stage. There is no spanish inquisition. - -As David Scherer aptly put it in the original IDLE fork README (below), -"It is alpha software and might be unstable. If it breaks, you get to -keep both pieces." One of the aims of IDLEfork now is for it to be able -to be uncompressed into its own directory and run from there, that way -you can play with (or hack on) IDLEfork without any further installation, -and entirely separately from your stable python IDLE distribution. - -If you find bugs or undesired behaviour please code nifty patches and -submit them to the IDLEfork SourceForge patch manager, 8^) or let us -know about it in one of the appropriate fora. See the IDLEfork home -page at - -http://idlefork.sourceforge.net - -for details on the various ways to give input to or contact the project. - -Please see the files NEWS.txt and ChangeLog for more up to date -information on changes in this release of IDLEfork. - - -Thanks for trying IDLEfork, -Stephen M. Gava. - - - - -README from IDLE fork 0.7.1 : -============================= - -EXPERIMENTAL LOADER IDLE 2000-05-29 ------------------------------------ - - David Scherer - -This is a modification of the CVS version of IDLE 0.5, updated as of -2000-03-09. It is alpha software and might be unstable. If it breaks, -you get to keep both pieces. - -If you have problems or suggestions, you should either contact me or -post to the list at http://www.python.org/mailman/listinfo/idle-dev -(making it clear that you are using this modified version of IDLE). - -Changes: - - The ExecBinding module, a replacement for ScriptBinding, executes - programs in a separate process, piping standard I/O through an RPC - mechanism to an OnDemandOutputWindow in IDLE. It supports executing - unnamed programs (through a temporary file). It does not yet support - debugging. - - When running programs with ExecBinding, tracebacks will be clipped - to exclude system modules. If, however, a system module calls back - into the user program, that part of the traceback will be shown. - - The OnDemandOutputWindow class has been improved. In particular, - it now supports a readline() function used to implement user input, - and a scroll_clear() operation which is used to hide the output of - a previous run by scrolling it out of the window. - - Startup behavior has been changed. By default IDLE starts up with - just a blank editor window, rather than an interactive window. Opening - a file in such a blank window replaces the (nonexistent) contents of - that window instead of creating another window. Because of the need to - have a well-known port for the ExecBinding protocol, only one copy of - IDLE can be running. Additional invocations use the RPC mechanism to - report their command line arguments to the copy already running. - - The menus have been reorganized. In particular, the excessively large - 'edit' menu has been split up into 'edit', 'format', and 'run'. - - 'Python Documentation' now works on Windows, if the win32api module is - present. - - A few key bindings have been changed: F1 now loads Python Documentation - instead of the IDLE help; shift-TAB is now a synonym for unindent. - -New modules: - ExecBinding.py Executes program through loader - loader.py Bootstraps user program - protocol.py RPC protocol - Remote.py User-process interpreter - spawn.py OS-specific code to start programs - -Files modified: - autoindent.py ( bindings tweaked ) - bindings.py ( menus reorganized ) - config.txt ( execbinding enabled ) - editorwindow.py ( new menus, fixed 'Python Documentation' ) - filelist.py ( hook for "open in same window" ) - formatparagraph.py ( bindings tweaked ) - idle.bat ( removed absolute pathname ) - idle.pyw ( weird bug due to import with same name? ) - iobinding.py ( open in same window, EOL convention ) - keydefs.py ( bindings tweaked ) - outputwindow.py ( readline, scroll_clear, etc ) - pyshell.py ( changed startup behavior ) - readme.txt ( ) - -IDLE 0.5 - February 2000 ------------------------- - -This is an early release of IDLE, my own attempt at a Tkinter-based -IDE for Python. - -For news about this release, see the file NEWS.txt. (For a more -detailed change log, see the file ChangeLog.) - -FEATURES - -IDLE has the following features: - -- coded in 100% pure Python, using the Tkinter GUI toolkit (i.e. Tcl/Tk) - -- cross-platform: works on Windows and Unix (on the Mac, there are -currently problems with Tcl/Tk) - -- multi-window text editor with multiple undo, Python colorizing -and many other features, e.g. smart indent and call tips - -- Python shell window (a.k.a. interactive interpreter) - -- debugger (not complete, but you can set breakpoints, view and step) - -USAGE - -The main program is in the file "idle.py"; on Unix, you should be able -to run it by typing "./idle.py" to your shell. On Windows, you can -run it by double-clicking it; you can use idle.pyw to avoid popping up -a DOS console. If you want to pass command line arguments on Windows, -use the batch file idle.bat. - -Command line arguments: files passed on the command line are executed, -not opened for editing, unless you give the -e command line option. -Try "./idle.py -h" to see other command line options. - -IDLE requires Python 1.5.2, so it is currently only usable with a -Python 1.5.2 distribution. (An older version of IDLE is distributed -with Python 1.5.2; you can drop this version on top of it.) - -COPYRIGHT - -IDLE is covered by the standard Python copyright notice -(http://www.python.org/doc/Copyright.html). - -FEEDBACK - -(removed, since Guido probably doesn't want complaints about my -changes) - ---Guido van Rossum (home page: http://www.python.org/~guido/) diff --git a/Lib/idlelib/Remote.py b/Lib/idlelib/Remote.py deleted file mode 100644 index facba78ce046..000000000000 --- a/Lib/idlelib/Remote.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Remote - This module is imported by the loader and serves to control - the execution of the user program. It presently executes files - and reports exceptions to IDLE. It could be extended to provide - other services, such as interactive mode and debugging. To that - end, it could be a subclass of e.g. InteractiveInterpreter. - - Two other classes, pseudoIn and pseudoOut, are file emulators also - used by loader. -""" -import sys, os -import traceback - -class Remote: - def __init__(self, main, master): - self.main = main - self.master = master - self.this_file = self.canonic( self.__init__.im_func.func_code.co_filename ) - - def canonic(self, path): - return os.path.normcase(os.path.abspath(path)) - - def mainloop(self): - while 1: - args = self.master.get_command() - - try: - f = getattr(self,args[0]) - apply(f,args[1:]) - except: - if not self.report_exception(): raise - - def finish(self): - sys.exit() - - def run(self, *argv): - sys.argv = argv - - path = self.canonic( argv[0] ) - dir = self.dir = os.path.dirname(path) - os.chdir(dir) - - sys.path[0] = dir - - usercode = open(path) - exec usercode in self.main - - def report_exception(self): - try: - type, value, tb = sys.exc_info() - sys.last_type = type - sys.last_value = value - sys.last_traceback = tb - - tblist = traceback.extract_tb(tb) - - # Look through the traceback, canonicalizing filenames and - # eliminating leading and trailing system modules. - first = last = 1 - for i in range(len(tblist)): - filename, lineno, name, line = tblist[i] - filename = self.canonic(filename) - tblist[i] = filename, lineno, name, line - - dir = os.path.dirname(filename) - if filename == self.this_file: - first = i+1 - elif dir==self.dir: - last = i+1 - - # Canonicalize the filename in a syntax error, too: - if type is SyntaxError: - try: - msg, (filename, lineno, offset, line) = value - filename = self.canonic(filename) - value = msg, (filename, lineno, offset, line) - except: - pass - - return self.master.program_exception( type, value, tblist, first, last ) - finally: - # avoid any circular reference through the traceback - del tb - -class pseudoIn: - def __init__(self, readline): - self.readline = readline - def isatty(): - return 1 - -class pseudoOut: - def __init__(self, func, **kw): - self.func = func - self.kw = kw - def write(self, *args): - return apply( self.func, args, self.kw ) - def writelines(self, l): - map(self.write, l) - def flush(self): - pass - diff --git a/Lib/idlelib/RemoteInterp.py b/Lib/idlelib/RemoteInterp.py deleted file mode 100644 index 724997c01037..000000000000 --- a/Lib/idlelib/RemoteInterp.py +++ /dev/null @@ -1,342 +0,0 @@ -import select -import socket -import struct -import sys -import types - -VERBOSE = None - -class SocketProtocol: - """A simple protocol for sending strings across a socket""" - BUF_SIZE = 8192 - - def __init__(self, sock): - self.sock = sock - self._buffer = '' - self._closed = 0 - - def close(self): - self._closed = 1 - self.sock.close() - - def send(self, buf): - """Encode buf and write it on the socket""" - if VERBOSE: - VERBOSE.write('send %d:%s\n' % (len(buf), `buf`)) - self.sock.send('%d:%s' % (len(buf), buf)) - - def receive(self, timeout=0): - """Get next complete string from socket or return None - - Raise EOFError on EOF - """ - buf = self._read_from_buffer() - if buf is not None: - return buf - recvbuf = self._read_from_socket(timeout) - if recvbuf is None: - return None - if recvbuf == '' and self._buffer == '': - raise EOFError - if VERBOSE: - VERBOSE.write('recv %s\n' % `recvbuf`) - self._buffer = self._buffer + recvbuf - r = self._read_from_buffer() - return r - - def _read_from_socket(self, timeout): - """Does not block""" - if self._closed: - return '' - if timeout is not None: - r, w, x = select.select([self.sock], [], [], timeout) - if timeout is None or r: - return self.sock.recv(self.BUF_SIZE) - else: - return None - - def _read_from_buffer(self): - buf = self._buffer - i = buf.find(':') - if i == -1: - return None - buflen = int(buf[:i]) - enclen = i + 1 + buflen - if len(buf) >= enclen: - s = buf[i+1:enclen] - self._buffer = buf[enclen:] - return s - else: - self._buffer = buf - return None - -# helpers for registerHandler method below - -def get_methods(obj): - methods = [] - for name in dir(obj): - attr = getattr(obj, name) - if callable(attr): - methods.append(name) - if type(obj) == types.InstanceType: - methods = methods + get_methods(obj.__class__) - if type(obj) == types.ClassType: - for super in obj.__bases__: - methods = methods + get_methods(super) - return methods - -class CommandProtocol: - def __init__(self, sockp): - self.sockp = sockp - self.seqno = 0 - self.handlers = {} - - def close(self): - self.sockp.close() - self.handlers.clear() - - def registerHandler(self, handler): - """A Handler is an object with handle_XXX methods""" - for methname in get_methods(handler): - if methname[:7] == "handle_": - name = methname[7:] - self.handlers[name] = getattr(handler, methname) - - def send(self, cmd, arg='', seqno=None): - if arg: - msg = "%s %s" % (cmd, arg) - else: - msg = cmd - if seqno is None: - seqno = self.get_seqno() - msgbuf = self.encode_seqno(seqno) + msg - self.sockp.send(msgbuf) - if cmd == "reply": - return - reply = self.sockp.receive(timeout=None) - r_cmd, r_arg, r_seqno = self._decode_msg(reply) - assert r_seqno == seqno and r_cmd == "reply", "bad reply" - return r_arg - - def _decode_msg(self, msg): - seqno = self.decode_seqno(msg[:self.SEQNO_ENC_LEN]) - msg = msg[self.SEQNO_ENC_LEN:] - parts = msg.split(" ", 2) - if len(parts) == 1: - cmd = msg - arg = '' - else: - cmd = parts[0] - arg = parts[1] - return cmd, arg, seqno - - def dispatch(self): - msg = self.sockp.receive() - if msg is None: - return - cmd, arg, seqno = self._decode_msg(msg) - self._current_reply = seqno - h = self.handlers.get(cmd, self.default_handler) - try: - r = h(arg) - except TypeError, msg: - raise TypeError, "handle_%s: %s" % (cmd, msg) - if self._current_reply is None: - if r is not None: - sys.stderr.write("ignoring %s return value type %s\n" % \ - (cmd, type(r).__name__)) - return - if r is None: - r = '' - if type(r) != types.StringType: - raise ValueError, "invalid return type for %s" % cmd - self.send("reply", r, seqno=seqno) - - def reply(self, arg=''): - """Send a reply immediately - - otherwise reply will be sent when handler returns - """ - self.send("reply", arg, self._current_reply) - self._current_reply = None - - def default_handler(self, arg): - sys.stderr.write("WARNING: unhandled message %s\n" % arg) - return '' - - SEQNO_ENC_LEN = 4 - - def get_seqno(self): - seqno = self.seqno - self.seqno = seqno + 1 - return seqno - - def encode_seqno(self, seqno): - return struct.pack("I", seqno) - - def decode_seqno(self, buf): - return struct.unpack("I", buf)[0] - - -class StdioRedirector: - """Redirect sys.std{in,out,err} to a set of file-like objects""" - - def __init__(self, stdin, stdout, stderr): - self.stdin = stdin - self.stdout = stdout - self.stderr = stderr - - def redirect(self): - self.save() - sys.stdin = self.stdin - sys.stdout = self.stdout - sys.stderr = self.stderr - - def save(self): - self._stdin = sys.stdin - self._stdout = sys.stdout - self._stderr = sys.stderr - - def restore(self): - sys.stdin = self._stdin - sys.stdout = self._stdout - sys.stderr = self._stderr - -class IOWrapper: - """Send output from a file-like object across a SocketProtocol - - XXX Should this be more tightly integrated with the CommandProtocol? - """ - - def __init__(self, name, cmdp): - self.name = name - self.cmdp = cmdp - self.buffer = [] - -class InputWrapper(IOWrapper): - def write(self, buf): - # XXX what should this do on Windows? - raise IOError, (9, '[Errno 9] Bad file descriptor') - - def read(self, arg=None): - if arg is not None: - if arg <= 0: - return '' - else: - arg = 0 - return self.cmdp.send(self.name, "read,%s" % arg) - - def readline(self): - return self.cmdp.send(self.name, "readline") - -class OutputWrapper(IOWrapper): - def write(self, buf): - self.cmdp.send(self.name, buf) - - def read(self, arg=None): - return '' - -class RemoteInterp: - def __init__(self, sock): - self._sock = SocketProtocol(sock) - self._cmd = CommandProtocol(self._sock) - self._cmd.registerHandler(self) - - def run(self): - try: - while 1: - self._cmd.dispatch() - except EOFError: - pass - - def handle_execfile(self, arg): - self._cmd.reply() - io = StdioRedirector(InputWrapper("stdin", self._cmd), - OutputWrapper("stdout", self._cmd), - OutputWrapper("stderr", self._cmd)) - io.redirect() - execfile(arg, {'__name__':'__main__'}) - io.restore() - self._cmd.send("terminated") - - def handle_quit(self, arg): - self._cmd.reply() - self._cmd.close() - -def startRemoteInterp(id): - import os - # UNIX domain sockets are simpler for starters - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.bind("/var/tmp/ri.%s" % id) - try: - sock.listen(1) - cli, addr = sock.accept() - rinterp = RemoteInterp(cli) - rinterp.run() - finally: - os.unlink("/var/tmp/ri.%s" % id) - -class RIClient: - """Client of the remote interpreter""" - def __init__(self, sock): - self._sock = SocketProtocol(sock) - self._cmd = CommandProtocol(self._sock) - self._cmd.registerHandler(self) - - def execfile(self, file): - self._cmd.send("execfile", file) - - def run(self): - try: - while 1: - self._cmd.dispatch() - except EOFError: - pass - - def handle_stdout(self, buf): - sys.stdout.write(buf) -## sys.stdout.flush() - - def handle_stderr(self, buf): - sys.stderr.write(buf) - - def handle_stdin(self, arg): - if arg == "readline": - return sys.stdin.readline() - i = arg.find(",") + 1 - bytes = int(arg[i:]) - if bytes == 0: - return sys.stdin.read() - else: - return sys.stdin.read(bytes) - - def handle_terminated(self, arg): - self._cmd.reply() - self._cmd.send("quit") - self._cmd.close() - -def riExec(id, file): - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect("/var/tmp/ri.%s" % id) - cli = RIClient(sock) - cli.execfile(file) - cli.run() - -if __name__ == "__main__": - import sys - import getopt - - SERVER = 1 - opts, args = getopt.getopt(sys.argv[1:], 'cv') - for o, v in opts: - if o == '-c': - SERVER = 0 - elif o == '-v': - VERBOSE = sys.stderr - id = args[0] - - if SERVER: - startRemoteInterp(id) - else: - file = args[1] - riExec(id, file) diff --git a/Lib/idlelib/ReplaceDialog.py b/Lib/idlelib/ReplaceDialog.py deleted file mode 100644 index 83462f9a1f79..000000000000 --- a/Lib/idlelib/ReplaceDialog.py +++ /dev/null @@ -1,188 +0,0 @@ -import string -import os -import re -import fnmatch -from Tkinter import * -import tkMessageBox -import SearchEngine -from SearchDialogBase import SearchDialogBase - -def replace(text): - root = text._root() - engine = SearchEngine.get(root) - if not hasattr(engine, "_replacedialog"): - engine._replacedialog = ReplaceDialog(root, engine) - dialog = engine._replacedialog - dialog.open(text) - -class ReplaceDialog(SearchDialogBase): - - title = "Replace Dialog" - icon = "Replace" - - def __init__(self, root, engine): - SearchDialogBase.__init__(self, root, engine) - self.replvar = StringVar(root) - - def open(self, text): - SearchDialogBase.open(self, text) - try: - first = text.index("sel.first") - except TclError: - first = None - try: - last = text.index("sel.last") - except TclError: - last = None - first = first or text.index("insert") - last = last or first - self.show_hit(first, last) - self.ok = 1 - - def create_entries(self): - SearchDialogBase.create_entries(self) - self.replent = self.make_entry("Replace with:", self.replvar) - - def create_command_buttons(self): - SearchDialogBase.create_command_buttons(self) - self.make_button("Find", self.find_it) - self.make_button("Replace", self.replace_it) - self.make_button("Replace+Find", self.default_command, 1) - self.make_button("Replace All", self.replace_all) - - def find_it(self, event=None): - self.do_find(0) - - def replace_it(self, event=None): - if self.do_find(self.ok): - self.do_replace() - - def default_command(self, event=None): - if self.do_find(self.ok): - self.do_replace() - self.do_find(0) - - def replace_all(self, event=None): - prog = self.engine.getprog() - if not prog: - return - repl = self.replvar.get() - text = self.text - res = self.engine.search_text(text, prog) - if not res: - text.bell() - return - text.tag_remove("sel", "1.0", "end") - text.tag_remove("hit", "1.0", "end") - line = res[0] - col = res[1].start() - if self.engine.iswrap(): - line = 1 - col = 0 - ok = 1 - first = last = None - # XXX ought to replace circular instead of top-to-bottom when wrapping - text.undo_block_start() - while 1: - res = self.engine.search_forward(text, prog, line, col, 0, ok) - if not res: - break - line, m = res - chars = text.get("%d.0" % line, "%d.0" % (line+1)) - orig = m.group() - new = self._expand(m, repl) - i, j = m.span() - first = "%d.%d" % (line, i) - last = "%d.%d" % (line, j) - if new == orig: - text.mark_set("insert", last) - else: - text.mark_set("insert", first) - if first != last: - text.delete(first, last) - if new: - text.insert(first, new) - col = i + len(new) - ok = 0 - text.undo_block_stop() - if first and last: - self.show_hit(first, last) - self.close() - - def do_find(self, ok=0): - if not self.engine.getprog(): - return 0 - text = self.text - res = self.engine.search_text(text, None, ok) - if not res: - text.bell() - return 0 - line, m = res - i, j = m.span() - first = "%d.%d" % (line, i) - last = "%d.%d" % (line, j) - self.show_hit(first, last) - self.ok = 1 - return 1 - - def do_replace(self): - prog = self.engine.getprog() - if not prog: - return 0 - text = self.text - try: - first = pos = text.index("sel.first") - last = text.index("sel.last") - except TclError: - pos = None - if not pos: - first = last = pos = text.index("insert") - line, col = SearchEngine.get_line_col(pos) - chars = text.get("%d.0" % line, "%d.0" % (line+1)) - m = prog.match(chars, col) - if not prog: - return 0 - new = self._expand(m, self.replvar.get()) - text.mark_set("insert", first) - text.undo_block_start() - if m.group(): - text.delete(first, last) - if new: - text.insert(first, new) - text.undo_block_stop() - self.show_hit(first, text.index("insert")) - self.ok = 0 - return 1 - - def _expand(self, m, template): - # XXX This code depends on internals of the regular expression - # engine! There's no standard API to do a substitution when you - # have already found the match. One should be added. - # The solution here is designed to be backwards compatible - # with previous Python versions, e.g. 1.5.2. - # XXX This dynamic test should be done only once. - if getattr(re, "engine", "pre") == "pre": - return re.pcre_expand(m, template) - else: # sre - # XXX This import should be avoidable... - import sre_parse - # XXX This parses the template over and over... - ptemplate = sre_parse.parse_template(template, m.re) - return sre_parse.expand_template(ptemplate, m) - - def show_hit(self, first, last): - text = self.text - text.mark_set("insert", first) - text.tag_remove("sel", "1.0", "end") - text.tag_add("sel", first, last) - text.tag_remove("hit", "1.0", "end") - if first == last: - text.tag_add("hit", first) - else: - text.tag_add("hit", first, last) - text.see("insert") - text.update_idletasks() - - def close(self, event=None): - SearchDialogBase.close(self, event) - self.text.tag_remove("hit", "1.0", "end") diff --git a/Lib/idlelib/ScriptBinding.py b/Lib/idlelib/ScriptBinding.py deleted file mode 100644 index b54dfc4c7919..000000000000 --- a/Lib/idlelib/ScriptBinding.py +++ /dev/null @@ -1,173 +0,0 @@ -"""Extension to execute code outside the Python shell window. - -This adds the following commands (to the Edit menu, until there's a -separate Python menu): - -- Check module (Alt-F5) does a full syntax check of the current module. -It also runs the tabnanny to catch any inconsistent tabs. - -- Import module (F5) is equivalent to either import or reload of the -current module. The window must have been saved previously. The -module is added to sys.modules, and is also added to the __main__ -namespace. Output goes to the shell window. - -- Run module (Control-F5) does the same but executes the module's -code in the __main__ namespace. - -""" - -import sys -import os -import imp -import tkMessageBox - -indent_message = """Error: Inconsistent indentation detected! - -This means that either: - -(1) your indentation is outright incorrect (easy to fix), or - -(2) your indentation mixes tabs and spaces in a way that depends on \ -how many spaces a tab is worth. - -To fix case 2, change all tabs to spaces by using Select All followed \ -by Untabify Region (both in the Edit menu).""" - -class ScriptBinding: - - keydefs = { - '<>': ['', ''], - '<>': [''], - '<>': [''], - } - - menudefs = [ - ('edit', [None, - ('Check module', '<>'), - ('Import module', '<>'), - ('Run script', '<>'), - ] - ), - ] - - def __init__(self, editwin): - self.editwin = editwin - # Provide instance variables referenced by Debugger - # XXX This should be done differently - self.flist = self.editwin.flist - self.root = self.flist.root - - def check_module_event(self, event): - filename = self.getfilename() - if not filename: - return - if not self.tabnanny(filename): - return - if not self.checksyntax(filename): - return - - def tabnanny(self, filename): - import tabnanny - import tokenize - tabnanny.reset_globals() - f = open(filename, 'r') - try: - tokenize.tokenize(f.readline, tabnanny.tokeneater) - except tokenize.TokenError, msg: - self.errorbox("Token error", - "Token error:\n%s" % str(msg)) - return 0 - except tabnanny.NannyNag, nag: - # The error messages from tabnanny are too confusing... - self.editwin.gotoline(nag.get_lineno()) - self.errorbox("Tab/space error", indent_message) - return 0 - return 1 - - def checksyntax(self, filename): - f = open(filename, 'r') - source = f.read() - f.close() - if '\r' in source: - import re - source = re.sub(r"\r\n", "\n", source) - if source and source[-1] != '\n': - source = source + '\n' - try: - compile(source, filename, "exec") - except (SyntaxError, OverflowError), err: - try: - msg, (errorfilename, lineno, offset, line) = err - if not errorfilename: - err.args = msg, (filename, lineno, offset, line) - err.filename = filename - except: - lineno = None - msg = "*** " + str(err) - if lineno: - self.editwin.gotoline(lineno) - self.errorbox("Syntax error", - "There's an error in your program:\n" + msg) - return 1 - - def import_module_event(self, event): - filename = self.getfilename() - if not filename: - return - - modname, ext = os.path.splitext(os.path.basename(filename)) - if sys.modules.has_key(modname): - mod = sys.modules[modname] - else: - mod = imp.new_module(modname) - sys.modules[modname] = mod - mod.__file__ = filename - setattr(sys.modules['__main__'], modname, mod) - - dir = os.path.dirname(filename) - dir = os.path.normpath(os.path.abspath(dir)) - if dir not in sys.path: - sys.path.insert(0, dir) - - flist = self.editwin.flist - shell = flist.open_shell() - interp = shell.interp - interp.runcode("reload(%s)" % modname) - - def run_script_event(self, event): - filename = self.getfilename() - if not filename: - return - - flist = self.editwin.flist - shell = flist.open_shell() - interp = shell.interp - if (not sys.argv or - os.path.basename(sys.argv[0]) != os.path.basename(filename)): - # XXX Too often this discards arguments the user just set... - sys.argv = [filename] - interp.execfile(filename) - - def getfilename(self): - # Logic to make sure we have a saved filename - # XXX Better logic would offer to save! - if not self.editwin.get_saved(): - name = (self.editwin.short_title() or - self.editwin.long_title() or - "Untitled") - self.errorbox("Not saved", - "The buffer for %s is not saved.\n" % name + - "Please save it first!") - self.editwin.text.focus_set() - return - filename = self.editwin.io.filename - if not filename: - self.errorbox("No file name", - "This window has no file name") - return - return filename - - 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() diff --git a/Lib/idlelib/ScrolledList.py b/Lib/idlelib/ScrolledList.py deleted file mode 100644 index 921193657705..000000000000 --- a/Lib/idlelib/ScrolledList.py +++ /dev/null @@ -1,139 +0,0 @@ -from Tkinter import * - -class ScrolledList: - - default = "(None)" - - def __init__(self, master, **options): - # Create top frame, with scrollbar and listbox - self.master = master - self.frame = frame = Frame(master) - self.frame.pack(fill="both", expand=1) - self.vbar = vbar = Scrollbar(frame, name="vbar") - self.vbar.pack(side="right", fill="y") - self.listbox = listbox = Listbox(frame, exportselection=0, - background="white") - if options: - listbox.configure(options) - listbox.pack(expand=1, fill="both") - # Tie listbox and scrollbar together - vbar["command"] = listbox.yview - listbox["yscrollcommand"] = vbar.set - # Bind events to the list box - listbox.bind("", self.click_event) - listbox.bind("", self.double_click_event) - listbox.bind("", self.popup_event) - listbox.bind("", self.up_event) - listbox.bind("", self.down_event) - # Mark as empty - self.clear() - - def close(self): - self.frame.destroy() - - def clear(self): - self.listbox.delete(0, "end") - self.empty = 1 - self.listbox.insert("end", self.default) - - def append(self, item): - if self.empty: - self.listbox.delete(0, "end") - self.empty = 0 - self.listbox.insert("end", str(item)) - - def get(self, index): - return self.listbox.get(index) - - def click_event(self, event): - self.listbox.activate("@%d,%d" % (event.x, event.y)) - index = self.listbox.index("active") - self.select(index) - self.on_select(index) - return "break" - - def double_click_event(self, event): - index = self.listbox.index("active") - self.select(index) - self.on_double(index) - return "break" - - menu = None - - def popup_event(self, event): - if not self.menu: - self.make_menu() - menu = self.menu - self.listbox.activate("@%d,%d" % (event.x, event.y)) - index = self.listbox.index("active") - self.select(index) - menu.tk_popup(event.x_root, event.y_root) - - def make_menu(self): - menu = Menu(self.listbox, tearoff=0) - self.menu = menu - self.fill_menu() - - def up_event(self, event): - index = self.listbox.index("active") - if self.listbox.selection_includes(index): - index = index - 1 - else: - index = self.listbox.size() - 1 - if index < 0: - self.listbox.bell() - else: - self.select(index) - self.on_select(index) - return "break" - - def down_event(self, event): - index = self.listbox.index("active") - if self.listbox.selection_includes(index): - index = index + 1 - else: - index = 0 - if index >= self.listbox.size(): - self.listbox.bell() - else: - self.select(index) - self.on_select(index) - return "break" - - def select(self, index): - self.listbox.focus_set() - self.listbox.activate(index) - self.listbox.selection_clear(0, "end") - self.listbox.selection_set(index) - self.listbox.see(index) - - # Methods to override for specific actions - - def fill_menu(self): - pass - - def on_select(self, index): - pass - - def on_double(self, index): - pass - - -def test(): - root = Tk() - root.protocol("WM_DELETE_WINDOW", root.destroy) - class MyScrolledList(ScrolledList): - def fill_menu(self): self.menu.add_command(label="pass") - def on_select(self, index): print "select", self.get(index) - def on_double(self, index): print "double", self.get(index) - s = MyScrolledList(root) - for i in range(30): - s.append("item %02d" % i) - return root - -def main(): - root = test() - root.mainloop() - -if __name__ == '__main__': - main() diff --git a/Lib/idlelib/SearchBinding.py b/Lib/idlelib/SearchBinding.py deleted file mode 100644 index 5943e3baec89..000000000000 --- a/Lib/idlelib/SearchBinding.py +++ /dev/null @@ -1,97 +0,0 @@ -import tkSimpleDialog - -###$ event <> -###$ win -###$ unix - -###$ event <> -###$ win -###$ win -###$ unix - -###$ event <> -###$ win -###$ unix - -###$ event <> -###$ win - -###$ event <> -###$ win - -###$ event <> -###$ win -###$ unix - -class SearchBinding: - - windows_keydefs = { - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - } - - unix_keydefs = { - '<>': [''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': ['', ''], - } - - menudefs = [ - ('edit', [ - None, - ('_Find...', '<>'), - ('Find a_gain', '<>'), - ('Find _selection', '<>'), - ('Find in Files...', '<>'), - ('R_eplace...', '<>'), - ('Go to _line', '<>'), - ]), - ] - - def __init__(self, editwin): - self.editwin = editwin - - def find_event(self, event): - import SearchDialog - SearchDialog.find(self.editwin.text) - return "break" - - def find_again_event(self, event): - import SearchDialog - SearchDialog.find_again(self.editwin.text) - return "break" - - def find_selection_event(self, event): - import SearchDialog - SearchDialog.find_selection(self.editwin.text) - return "break" - - def find_in_files_event(self, event): - import GrepDialog - GrepDialog.grep(self.editwin.text, self.editwin.io, self.editwin.flist) - return "break" - - def replace_event(self, event): - import ReplaceDialog - ReplaceDialog.replace(self.editwin.text) - return "break" - - def goto_line_event(self, event): - text = self.editwin.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") diff --git a/Lib/idlelib/SearchDialog.py b/Lib/idlelib/SearchDialog.py deleted file mode 100644 index 0f0cb189f6b7..000000000000 --- a/Lib/idlelib/SearchDialog.py +++ /dev/null @@ -1,67 +0,0 @@ -from Tkinter import * -import SearchEngine -from SearchDialogBase import SearchDialogBase - - -def _setup(text): - root = text._root() - engine = SearchEngine.get(root) - if not hasattr(engine, "_searchdialog"): - engine._searchdialog = SearchDialog(root, engine) - return engine._searchdialog - -def find(text): - return _setup(text).open(text) - -def find_again(text): - return _setup(text).find_again(text) - -def find_selection(text): - return _setup(text).find_selection(text) - -class SearchDialog(SearchDialogBase): - - def create_widgets(self): - f = SearchDialogBase.create_widgets(self) - self.make_button("Find", self.default_command, 1) - - def default_command(self, event=None): - if not self.engine.getprog(): - return - if self.find_again(self.text): - self.close() - - def find_again(self, text): - if not self.engine.getpat(): - self.open(text) - return 0 - if not self.engine.getprog(): - return 0 - res = self.engine.search_text(text) - if res: - line, m = res - i, j = m.span() - first = "%d.%d" % (line, i) - last = "%d.%d" % (line, j) - try: - selfirst = text.index("sel.first") - sellast = text.index("sel.last") - if selfirst == first and sellast == last: - text.bell() - return 0 - except TclError: - pass - text.tag_remove("sel", "1.0", "end") - text.tag_add("sel", first, last) - text.mark_set("insert", self.engine.isback() and first or last) - text.see("insert") - return 1 - else: - text.bell() - return 0 - - def find_selection(self, text): - pat = text.get("sel.first", "sel.last") - if pat: - self.engine.setcookedpat(pat) - return self.find_again(text) diff --git a/Lib/idlelib/SearchDialogBase.py b/Lib/idlelib/SearchDialogBase.py deleted file mode 100644 index faf526918f36..000000000000 --- a/Lib/idlelib/SearchDialogBase.py +++ /dev/null @@ -1,129 +0,0 @@ -import string -from Tkinter import * - -class SearchDialogBase: - - title = "Search Dialog" - icon = "Search" - needwrapbutton = 1 - - def __init__(self, root, engine): - self.root = root - self.engine = engine - self.top = None - - def open(self, text): - self.text = text - if not self.top: - self.create_widgets() - else: - self.top.deiconify() - self.top.tkraise() - self.ent.focus_set() - self.ent.selection_range(0, "end") - self.ent.icursor(0) - self.top.grab_set() - - def close(self, event=None): - if self.top: - self.top.grab_release() - self.top.withdraw() - - def create_widgets(self): - top = Toplevel(self.root) - top.bind("", self.default_command) - top.bind("", self.close) - top.protocol("WM_DELETE_WINDOW", self.close) - top.wm_title(self.title) - top.wm_iconname(self.icon) - self.top = top - - self.row = 0 - self.top.grid_columnconfigure(0, weight=0) - self.top.grid_columnconfigure(1, weight=100) - - self.create_entries() - self.create_option_buttons() - self.create_other_buttons() - return self.create_command_buttons() - - def make_entry(self, label, var): - l = Label(self.top, text=label) - l.grid(row=self.row, col=0, sticky="w") - e = Entry(self.top, textvariable=var, exportselection=0) - e.grid(row=self.row, col=1, sticky="we") - self.row = self.row + 1 - return e - - def make_frame(self): - f = Frame(self.top) - f.grid(row=self.row, col=0, columnspan=2, sticky="we") - self.row = self.row + 1 - return f - - def make_button(self, label, command, isdef=0, side="left"): - b = Button(self.buttonframe, - text=label, command=command, - default=isdef and "active" or "normal") - b.pack(side=side) - return b - - def create_entries(self): - self.ent = self.make_entry("Find:", self.engine.patvar) - - def create_option_buttons(self): - f = self.make_frame() - - btn = Checkbutton(f, anchor="w", - variable=self.engine.revar, - text="Regular expression") - btn.pack(side="left", fill="both") - if self.engine.isre(): - btn.select() - - btn = Checkbutton(f, anchor="w", - variable=self.engine.casevar, - text="Match case") - btn.pack(side="left", fill="both") - if self.engine.iscase(): - btn.select() - - btn = Checkbutton(f, anchor="w", - variable=self.engine.wordvar, - text="Whole word") - btn.pack(side="left", fill="both") - if self.engine.isword(): - btn.select() - - if self.needwrapbutton: - btn = Checkbutton(f, anchor="w", - variable=self.engine.wrapvar, - text="Wrap around") - btn.pack(side="left", fill="both") - if self.engine.iswrap(): - btn.select() - - def create_other_buttons(self): - f = self.make_frame() - - 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") - if self.engine.isback(): - btn.select() - - btn = Radiobutton(f, anchor="w", - variable=self.engine.backvar, value=0, - text="Down") - btn.pack(side="left", fill="both") - if not self.engine.isback(): - btn.select() - - def create_command_buttons(self): - f = self.buttonframe = self.make_frame() - b = self.make_button("close", self.close, side="right") - b.lower() diff --git a/Lib/idlelib/SearchEngine.py b/Lib/idlelib/SearchEngine.py deleted file mode 100644 index e37975104abc..000000000000 --- a/Lib/idlelib/SearchEngine.py +++ /dev/null @@ -1,221 +0,0 @@ -import string -import re -from Tkinter import * -import tkMessageBox - -def get(root): - if not hasattr(root, "_searchengine"): - root._searchengine = SearchEngine(root) - # XXX This will never garbage-collect -- who cares - return root._searchengine - -class SearchEngine: - - def __init__(self, root): - self.root = root - # State shared by search, replace, and grep; - # the search dialogs bind these to UI elements. - self.patvar = StringVar(root) # search pattern - self.revar = BooleanVar(root) # regular expression? - self.casevar = BooleanVar(root) # match case? - self.wordvar = BooleanVar(root) # match whole word? - self.wrapvar = BooleanVar(root) # wrap around buffer? - self.wrapvar.set(1) # (on by default) - self.backvar = BooleanVar(root) # search backwards? - - # Access methods - - def getpat(self): - return self.patvar.get() - - def setpat(self, pat): - self.patvar.set(pat) - - def isre(self): - return self.revar.get() - - def iscase(self): - return self.casevar.get() - - def isword(self): - return self.wordvar.get() - - def iswrap(self): - return self.wrapvar.get() - - def isback(self): - return self.backvar.get() - - # Higher level access methods - - def getcookedpat(self): - pat = self.getpat() - if not self.isre(): - pat = re.escape(pat) - if self.isword(): - pat = r"\b%s\b" % pat - return pat - - def getprog(self): - pat = self.getpat() - if not pat: - self.report_error(pat, "Empty regular expression") - return None - pat = self.getcookedpat() - flags = 0 - if not self.iscase(): - flags = flags | re.IGNORECASE - try: - prog = re.compile(pat, flags) - except re.error, what: - try: - msg, col = what - except: - msg = str(what) - col = -1 - self.report_error(pat, msg, col) - return None - return prog - - def report_error(self, pat, msg, col=-1): - # Derived class could overrid this with something fancier - msg = "Error: " + str(msg) - if pat: - msg = msg + "\np\Pattern: " + str(pat) - if col >= 0: - msg = msg + "\nOffset: " + str(col) - tkMessageBox.showerror("Regular expression error", - msg, master=self.root) - - def setcookedpat(self, pat): - if self.isre(): - pat = re.escape(pat) - self.setpat(pat) - - def search_text(self, text, prog=None, ok=0): - """Search a text widget for the pattern. - - If prog is given, it should be the precompiled pattern. - Return a tuple (lineno, matchobj); None if not found. - - This obeys the wrap and direction (back) settings. - - The search starts at the selection (if there is one) or - at the insert mark (otherwise). If the search is forward, - it starts at the right of the selection; for a backward - search, it starts at the left end. An empty match exactly - at either end of the selection (or at the insert mark if - there is no selection) is ignored unless the ok flag is true - -- this is done to guarantee progress. - - If the search is allowed to wrap around, it will return the - original selection if (and only if) it is the only match. - - """ - if not prog: - prog = self.getprog() - if not prog: - return None # Compilation failed -- stop - wrap = self.wrapvar.get() - first, last = get_selection(text) - if self.isback(): - if ok: - start = last - else: - start = first - line, col = get_line_col(start) - res = self.search_backward(text, prog, line, col, wrap, ok) - else: - if ok: - start = first - else: - start = last - line, col = get_line_col(start) - res = self.search_forward(text, prog, line, col, wrap, ok) - return res - - def search_forward(self, text, prog, line, col, wrap, ok=0): - wrapped = 0 - startline = line - chars = text.get("%d.0" % line, "%d.0" % (line+1)) - while chars: - m = prog.search(chars[:-1], col) - if m: - if ok or m.end() > col: - return line, m - line = line + 1 - if wrapped and line > startline: - break - col = 0 - ok = 1 - chars = text.get("%d.0" % line, "%d.0" % (line+1)) - if not chars and wrap: - wrapped = 1 - wrap = 0 - line = 1 - chars = text.get("1.0", "2.0") - return None - - def search_backward(self, text, prog, line, col, wrap, ok=0): - wrapped = 0 - startline = line - chars = text.get("%d.0" % line, "%d.0" % (line+1)) - while 1: - m = search_reverse(prog, chars[:-1], col) - if m: - if ok or m.start() < col: - return line, m - line = line - 1 - if wrapped and line < startline: - break - ok = 1 - if line <= 0: - if not wrap: - break - wrapped = 1 - wrap = 0 - pos = text.index("end-1c") - line, col = map(int, string.split(pos, ".")) - chars = text.get("%d.0" % line, "%d.0" % (line+1)) - col = len(chars) - 1 - return None - -# Helper to search backwards in a string. -# (Optimized for the case where the pattern isn't found.) - -def search_reverse(prog, chars, col): - m = prog.search(chars) - if not m: - return None - found = None - i, j = m.span() - while i < col and j <= col: - found = m - if i == j: - j = j+1 - m = prog.search(chars, j) - if not m: - break - i, j = m.span() - return found - -# Helper to get selection end points, defaulting to insert mark. -# Return a tuple of indices ("line.col" strings). - -def get_selection(text): - try: - first = text.index("sel.first") - last = text.index("sel.last") - except TclError: - first = last = None - if not first: - first = text.index("insert") - if not last: - last = first - return first, last - -# Helper to parse a text index into a (line, col) tuple. - -def get_line_col(index): - line, col = map(int, string.split(index, ".")) # Fails on invalid index - return line, col diff --git a/Lib/idlelib/Separator.py b/Lib/idlelib/Separator.py deleted file mode 100644 index 7145559c7b97..000000000000 --- a/Lib/idlelib/Separator.py +++ /dev/null @@ -1,92 +0,0 @@ -from Tkinter import * - -class Separator: - - def __init__(self, master, orient, min=10, thickness=5, bg=None): - self.min = max(1, min) - self.thickness = max(1, thickness) - if orient in ("h", "horizontal"): - self.side = "left" - self.dim = "width" - self.dir = "x" - self.cursor = "sb_h_double_arrow" - elif orient in ("v", "vertical"): - self.side = "top" - self.dim = "height" - self.dir = "y" - self.cursor = "sb_v_double_arrow" - else: - raise ValueError, "Separator: orient should be h or v" - self.winfo_dim = "winfo_" + self.dim - self.master = master = Frame(master) - master.pack(expand=1, fill="both") - self.f1 = Frame(master) - self.f1.pack(expand=1, fill="both", side=self.side) - self.div = Frame(master, cursor=self.cursor) - self.div[self.dim] = self.thickness - self.div.pack(fill="both", side=self.side) - self.f2 = Frame(master) - self.f2.pack(expand=1, fill="both", side=self.side) - self.div.bind("", self.divider_press) - if bg: - ##self.f1["bg"] = bg - ##self.f2["bg"] = bg - self.div["bg"] = bg - - def parts(self): - return self.f1, self.f2 - - def divider_press(self, event): - self.press_event = event - self.f1.pack_propagate(0) - self.f2.pack_propagate(0) - for f in self.f1, self.f2: - for dim in "width", "height": - f[dim] = getattr(f, "winfo_"+dim)() - self.div.bind("", self.div_motion) - self.div.bind("", self.div_release) - self.div.grab_set() - - def div_motion(self, event): - delta = getattr(event, self.dir) - getattr(self.press_event, self.dir) - if delta: - dim1 = getattr(self.f1, self.winfo_dim)() - dim2 = getattr(self.f2, self.winfo_dim)() - delta = max(delta, self.min-dim1) - delta = min(delta, dim2-self.min) - dim1 = dim1 + delta - dim2 = dim2 - delta - self.f1[self.dim] = dim1 - self.f2[self.dim] = dim2 - - def div_release(self, event): - self.div_motion(event) - self.div.unbind("") - self.div.grab_release() - -class VSeparator(Separator): - - def __init__(self, master, min=10, thickness=5, bg=None): - Separator.__init__(self, master, "v", min, thickness, bg) - -class HSeparator(Separator): - - def __init__(self, master, min=10, thickness=5, bg=None): - Separator.__init__(self, master, "h", min, thickness, bg) - -def main(): - root = Tk() - tlist = [] - outer = HSeparator(root, bg="red") - for part in outer.parts(): - inner = VSeparator(part, bg="blue") - for f in inner.parts(): - t = Text(f, width=40, height=10, borderwidth=0) - t.pack(fill="both", expand=1) - tlist.append(t) - tlist[0].insert("1.0", "Make your own Mondrian!") - tlist[1].insert("1.0", "Move the colored dividers...") - root.mainloop() - -if __name__ == '__main__': - main() diff --git a/Lib/idlelib/StackViewer.py b/Lib/idlelib/StackViewer.py deleted file mode 100644 index d70658bcbbde..000000000000 --- a/Lib/idlelib/StackViewer.py +++ /dev/null @@ -1,147 +0,0 @@ -import os -import sys -import string -import linecache - -from TreeWidget import TreeNode, TreeItem, ScrolledCanvas -from ObjectBrowser import ObjectTreeItem, make_objecttreeitem -from OldStackViewer import StackViewer, NamespaceViewer - -def StackBrowser(root, flist=None, tb=None, top=None): - if top is None: - from Tkinter import Toplevel - top = Toplevel(root) - sc = ScrolledCanvas(top, bg="white", highlightthickness=0) - sc.frame.pack(expand=1, fill="both") - item = StackTreeItem(flist, tb) - node = TreeNode(sc.canvas, None, item) - node.expand() - -class StackTreeItem(TreeItem): - - def __init__(self, flist=None, tb=None): - self.flist = flist - self.stack = get_stack(tb) - self.text = get_exception() - - def GetText(self): - return self.text - - def GetSubList(self): - sublist = [] - for info in self.stack: - item = FrameTreeItem(info, self.flist) - sublist.append(item) - return sublist - -class FrameTreeItem(TreeItem): - - def __init__(self, info, flist): - self.info = info - self.flist = flist - - def GetText(self): - frame, lineno = self.info - try: - modname = frame.f_globals["__name__"] - except: - modname = "?" - code = frame.f_code - filename = code.co_filename - funcname = code.co_name - sourceline = linecache.getline(filename, lineno) - sourceline = string.strip(sourceline) - if funcname in ("?", "", None): - item = "%s, line %d: %s" % (modname, lineno, sourceline) - else: - item = "%s.%s(...), line %d: %s" % (modname, funcname, - lineno, sourceline) -## if i == index: -## item = "> " + item - return item - - def GetSubList(self): - frame, lineno = self.info - sublist = [] - if frame.f_globals is not frame.f_locals: - item = VariablesTreeItem("", frame.f_locals, self.flist) - sublist.append(item) - item = VariablesTreeItem("", frame.f_globals, self.flist) - sublist.append(item) - return sublist - - def OnDoubleClick(self): - if self.flist: - frame, lineno = self.info - filename = frame.f_code.co_filename - if os.path.isfile(filename): - self.flist.gotofileline(filename, lineno) - -class VariablesTreeItem(ObjectTreeItem): - - def GetText(self): - return self.labeltext - - def GetLabelText(self): - return None - - def IsExpandable(self): - return len(self.object) > 0 - - def keys(self): - return self.object.keys() - - def GetSubList(self): - sublist = [] - for key in self.keys(): - try: - value = self.object[key] - except KeyError: - continue - def setfunction(value, key=key, object=self.object): - object[key] = value - item = make_objecttreeitem(key + " =", value, setfunction) - sublist.append(item) - return sublist - -def get_stack(t=None, f=None): - if t is None: - t = sys.last_traceback - stack = [] - if t and t.tb_frame is f: - t = t.tb_next - while f is not None: - stack.append((f, f.f_lineno)) - if f is self.botframe: - break - f = f.f_back - stack.reverse() - while t is not None: - stack.append((t.tb_frame, t.tb_lineno)) - t = t.tb_next - return stack - -def get_exception(type=None, value=None): - if type is None: - type = sys.last_type - value = sys.last_value - if hasattr(type, "__name__"): - type = type.__name__ - s = str(type) - if value is not None: - s = s + ": " + str(value) - return s - -def _test(): - try: - import testcode - reload(testcode) - except: - sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() - from Tkinter import Tk - root = Tk() - StackBrowser(None, top=root) - root.mainloop() - -if __name__ == "__main__": - _test() diff --git a/Lib/idlelib/TODO.txt b/Lib/idlelib/TODO.txt deleted file mode 100644 index 96b045020296..000000000000 --- a/Lib/idlelib/TODO.txt +++ /dev/null @@ -1,212 +0,0 @@ -Original IDLE todo, much of it now outdated: -============================================ -TO DO: - -- improve debugger: - - manage breakpoints globally, allow bp deletion, tbreak, cbreak etc. - - real object browser - - help on how to use it (a simple help button will do wonders) - - performance? (updates of large sets of locals are slow) - - better integration of "debug module" - - debugger should be global resource (attached to flist, not to shell) - - fix the stupid bug where you need to step twice - - display class name in stack viewer entries for methods - - suppress tracing through IDLE internals (e.g. print) - - add a button to suppress through a specific module or class or method - - more object inspection to stack viewer, e.g. to view all array items -- insert the initial current directory into sys.path -- default directory attribute for each window instead of only for windows - that have an associated filename -- command expansion from keywords, module contents, other buffers, etc. -- "Recent documents" menu item -- Filter region command -- Optional horizontal scroll bar -- more Emacsisms: - - ^K should cut to buffer - - M-[, M-] to move by paragraphs - - incremental search? -- search should indicate wrap-around in some way -- restructure state sensitive code to avoid testing flags all the time -- persistent user state (e.g. window and cursor positions, bindings) -- make backups when saving -- check file mtimes at various points -- Pluggable interface with RCS/CVS/Perforce/Clearcase -- better help? -- don't open second class browser on same module (nor second path browser) -- unify class and path browsers -- Need to define a standard way whereby one can determine one is running - inside IDLE (needed for Tk mainloop, also handy for $PYTHONSTARTUP) -- Add more utility methods for use by extensions (a la get_selection) -- Way to run command in totally separate interpreter (fork+os.system?) -- Way to find definition of fully-qualified name: - In other words, select "UserDict.UserDict", hit some magic key and - it loads up UserDict.py and finds the first def or class for UserDict. -- need a way to force colorization on/off -- need a way to force auto-indent on/off - -Details: - -- when there's a selection, left/right arrow should go to either - end of the selection -- ^O (on Unix -- open-line) should honor autoindent -- after paste, show end of pasted text -- on Windows, should turn short filename to long filename (not only in argv!) - (shouldn't this be done -- or undone -- by ntpath.normpath?) -- new autoindent after colon even indents when the colon is in a comment! -- sometimes forward slashes in pathname remain -- sometimes star in window name remains in Windows menu -- With unix bindings, ESC by itself is ignored -- Sometimes for no apparent reason a selection from the cursor to the - end of the command buffer appears, which is hard to get rid of - because it stays when you are typing! -- The Line/Col in the status bar can be wrong initially in PyShell - -Structural problems: - -- too much knowledge in FileList about EditorWindow (for example) -- should add some primitives for accessing the selection etc. - to repeat cumbersome code over and over - -====================================================================== - -Jeff Bauer suggests: - -- Open Module doesn't appear to handle hierarchical packages. -- Class browser should also allow hierarchical packages. -- Open and Open Module could benefit from a history, - either command line style, or Microsoft recent-file - style. -- Add a Smalltalk-style inspector (i.e. Tkinspect) - -The last suggestion is already a reality, but not yet -integrated into IDLE. I use a module called inspector.py, -that used to be available from python.org(?) It no longer -appears to be in the contributed section, and the source -has no author attribution. - -In any case, the code is useful for visually navigating -an object's attributes, including its container hierarchy. - - >>> from inspector import Tkinspect - >>> Tkinspect(None, myObject) - -Tkinspect could probably be extended and refined to -integrate better into IDLE. - -====================================================================== - -Comparison to PTUI ------------------- - -+ PTUI's help is better (HTML!) - -+ PTUI can attach a shell to any module - -+ PTUI has some more I/O commands: - open multiple - append - examine (what's that?) - -====================================================================== - -Notes after trying to run Grail -------------------------------- - -- Grail does stuff to sys.path based on sys.argv[0]; you must set -sys.argv[0] to something decent first (it is normally set to the path of -the idle script). - -- Grail must be exec'ed in __main__ because that's imported by some -other parts of Grail. - -- Grail uses a module called History and so does idle :-( - -====================================================================== - -Robin Friedrich's items: - -Things I'd like to see: - - I'd like support for shift-click extending the selection. There's a - bug now that it doesn't work the first time you try it. - - Printing is needed. How hard can that be on Windows? - - The python-mode trick of autoindenting a line with is neat and - very handy. - - (someday) a spellchecker for docstrings and comments. - - a pagedown/up command key which moves to next class/def statement (top - level) - - split window capability - - DnD text relocation/copying - -Things I don't want to see. - - line numbers... will probably slow things down way too much. - - Please use another icon for the tree browser leaf. The small snake - isn't cutting it. - ----------------------------------------------------------------------- - -- Customizable views (multi-window or multi-pane). (Markus Gritsch) - -- Being able to double click (maybe double right click) on a callable -object in the editor which shows the source of the object, if -possible. (Gerrit Holl) - -- Hooks into the guts, like in Emacs. (Mike Romberg) - -- Sharing the editor with a remote tutor. (Martijn Faassen) - -- Multiple views on the same file. (Tony J Ibbs) - -- Store breakpoints in a global (per-project) database (GvR); Dirk -Heise adds: save some space-trimmed context and search around when -reopening a file that might have been edited by someone else. - -- Capture menu events in extensions without changing the IDLE source. -(Matthias Barmeier) - -- Use overlapping panels (a "notebook" in MFC terms I think) for info -that doesn't need to be accessible simultaneously (e.g. HTML source -and output). Use multi-pane windows for info that does need to be -shown together (e.g. class browser and source). (Albert Brandl) - -- A project should invisibly track all symbols, for instant search, -replace and cross-ref. Projects should be allowed to span multiple -directories, hosts, etc. Project management files are placed in a -directory you specify. A global mapping between project names and -project directories should exist [not so sure --GvR]. (Tim Peters) - -- Merge attr-tips and auto-expand. (Mark Hammond, Tim Peters) - -- Python Shell should behave more like a "shell window" as users know -it -- i.e. you can only edit the current command, and the cursor can't -escape from the command area. (Albert Brandl) - -- Set X11 class to "idle/Idle", set icon and title to something -beginning with "idle" -- for window manangers. (Randall Hopper) - -- Config files editable through a preferences dialog. (me) - -- Config files still editable outside the preferences dialog. -(Randall Hopper) - -- When you're editing a command in PyShell, and there are only blank -lines below the cursor, hitting Return should ignore or delete those -blank lines rather than deciding you're not on the last line. (me) - -- Run command (F5 c.s.) should be more like Pythonwin's Run -- a -dialog with options to give command line arguments, run the debugger, -etc. (me) - -- Shouldn't be able to delete part of the prompt (or any text before -it) in the PyShell. (Martijn Faassen) - -- Emacs style auto-fill (also smart about comments and strings). -(Jeremy Hylton) - -- Output of Run Script should go to a separate output window, not to -the shell window. Output of separate runs should all go to the same -window but clearly delimited. (David Scherer) - -- GUI form designer to kick VB's butt. (Robert Geiger) - -- Printing! Possibly via generation of PDF files which the user must -then send to the printer separately. (Dinu Gherman) diff --git a/Lib/idlelib/ToolTip.py b/Lib/idlelib/ToolTip.py deleted file mode 100644 index eadcdea5a14f..000000000000 --- a/Lib/idlelib/ToolTip.py +++ /dev/null @@ -1,87 +0,0 @@ -# Ideas gleaned from PySol - -import os -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() # not in idle - -main() diff --git a/Lib/idlelib/TreeWidget.py b/Lib/idlelib/TreeWidget.py deleted file mode 100644 index 60eefdc220ec..000000000000 --- a/Lib/idlelib/TreeWidget.py +++ /dev/null @@ -1,471 +0,0 @@ -# XXX TO DO: -# - popup menu -# - support partial or total redisplay -# - key bindings (instead of quick-n-dirty bindings on Canvas): -# - up/down arrow keys to move focus around -# - ditto for page up/down, home/end -# - left/right arrows to expand/collapse & move out/in -# - more doc strings -# - add icons for "file", "module", "class", "method"; better "python" icon -# - callback for selection??? -# - multiple-item selection -# - tooltips -# - redo geometry without magic numbers -# - keep track of object ids to allow more careful cleaning -# - optimize tree redraw after expand of subnode - -import os -import sys -import string -from Tkinter import * -import imp - -import ZoomHeight - -ICONDIR = "Icons" - -# Look for Icons subdirectory in the same directory as this module -try: - _icondir = os.path.join(os.path.dirname(__file__), ICONDIR) -except NameError: - _icondir = ICONDIR -if os.path.isdir(_icondir): - ICONDIR = _icondir -elif not os.path.isdir(ICONDIR): - raise RuntimeError, "can't find icon directory (%s)" % `ICONDIR` - -def listicons(icondir=ICONDIR): - """Utility to display the available icons.""" - root = Tk() - import glob - list = glob.glob(os.path.join(icondir, "*.gif")) - list.sort() - images = [] - row = column = 0 - for file in list: - name = os.path.splitext(os.path.basename(file))[0] - image = PhotoImage(file=file, master=root) - images.append(image) - label = Label(root, image=image, bd=1, relief="raised") - label.grid(row=row, column=column) - label = Label(root, text=name) - label.grid(row=row+1, column=column) - column = column + 1 - if column >= 10: - row = row+2 - column = 0 - root.images = images - - -class TreeNode: - - def __init__(self, canvas, parent, item): - self.canvas = canvas - self.parent = parent - self.item = item - self.state = 'collapsed' - self.selected = 0 - self.children = [] - self.x = self.y = None - self.iconimages = {} # cache of PhotoImage instances for icons - - def destroy(self): - for c in self.children[:]: - self.children.remove(c) - c.destroy() - self.parent = None - - def geticonimage(self, name): - try: - return self.iconimages[name] - except KeyError: - pass - file, ext = os.path.splitext(name) - ext = ext or ".gif" - fullname = os.path.join(ICONDIR, file + ext) - image = PhotoImage(master=self.canvas, file=fullname) - self.iconimages[name] = image - return image - - def select(self, event=None): - if self.selected: - return - self.deselectall() - self.selected = 1 - self.canvas.delete(self.image_id) - self.drawicon() - self.drawtext() - - def deselect(self, event=None): - if not self.selected: - return - self.selected = 0 - self.canvas.delete(self.image_id) - self.drawicon() - self.drawtext() - - def deselectall(self): - if self.parent: - self.parent.deselectall() - else: - self.deselecttree() - - def deselecttree(self): - if self.selected: - self.deselect() - for child in self.children: - child.deselecttree() - - def flip(self, event=None): - if self.state == 'expanded': - self.collapse() - else: - self.expand() - self.item.OnDoubleClick() - return "break" - - def expand(self, event=None): - if not self.item._IsExpandable(): - return - if self.state != 'expanded': - self.state = 'expanded' - self.update() - self.view() - - def collapse(self, event=None): - if self.state != 'collapsed': - self.state = 'collapsed' - self.update() - - def view(self): - top = self.y - 2 - bottom = self.lastvisiblechild().y + 17 - height = bottom - top - visible_top = self.canvas.canvasy(0) - visible_height = self.canvas.winfo_height() - visible_bottom = self.canvas.canvasy(visible_height) - if visible_top <= top and bottom <= visible_bottom: - return - x0, y0, x1, y1 = self.canvas._getints(self.canvas['scrollregion']) - if top >= visible_top and height <= visible_height: - fraction = top + height - visible_height - else: - fraction = top - fraction = float(fraction) / y1 - self.canvas.yview_moveto(fraction) - - def lastvisiblechild(self): - if self.children and self.state == 'expanded': - return self.children[-1].lastvisiblechild() - else: - return self - - def update(self): - if self.parent: - self.parent.update() - else: - oldcursor = self.canvas['cursor'] - self.canvas['cursor'] = "watch" - self.canvas.update() - self.canvas.delete(ALL) # XXX could be more subtle - self.draw(7, 2) - x0, y0, x1, y1 = self.canvas.bbox(ALL) - self.canvas.configure(scrollregion=(0, 0, x1, y1)) - self.canvas['cursor'] = oldcursor - - def draw(self, x, y): - # XXX This hard-codes too many geometry constants! - self.x, self.y = x, y - self.drawicon() - self.drawtext() - if self.state != 'expanded': - return y+17 - # draw children - if not self.children: - sublist = self.item._GetSubList() - if not sublist: - # _IsExpandable() was mistaken; that's allowed - return y+17 - for item in sublist: - child = TreeNode(self.canvas, self, item) - self.children.append(child) - cx = x+20 - cy = y+17 - cylast = 0 - for child in self.children: - cylast = cy - self.canvas.create_line(x+9, cy+7, cx, cy+7, fill="gray50") - cy = child.draw(cx, cy) - if child.item._IsExpandable(): - if child.state == 'expanded': - iconname = "minusnode" - callback = child.collapse - else: - iconname = "plusnode" - callback = child.expand - image = self.geticonimage(iconname) - id = self.canvas.create_image(x+9, cylast+7, image=image) - # XXX This leaks bindings until canvas is deleted: - self.canvas.tag_bind(id, "<1>", callback) - self.canvas.tag_bind(id, "", lambda x: None) - id = self.canvas.create_line(x+9, y+10, x+9, cylast+7, - ##stipple="gray50", # XXX Seems broken in Tk 8.0.x - fill="gray50") - self.canvas.tag_lower(id) # XXX .lower(id) before Python 1.5.2 - return cy - - def drawicon(self): - if self.selected: - imagename = (self.item.GetSelectedIconName() or - self.item.GetIconName() or - "openfolder") - else: - imagename = self.item.GetIconName() or "folder" - image = self.geticonimage(imagename) - id = self.canvas.create_image(self.x, self.y, anchor="nw", image=image) - self.image_id = id - self.canvas.tag_bind(id, "<1>", self.select) - self.canvas.tag_bind(id, "", self.flip) - - def drawtext(self): - textx = self.x+20-1 - texty = self.y-1 - labeltext = self.item.GetLabelText() - if labeltext: - id = self.canvas.create_text(textx, texty, anchor="nw", - text=labeltext) - self.canvas.tag_bind(id, "<1>", self.select) - self.canvas.tag_bind(id, "", self.flip) - x0, y0, x1, y1 = self.canvas.bbox(id) - textx = max(x1, 200) + 10 - text = self.item.GetText() or "" - try: - self.entry - except AttributeError: - pass - else: - self.edit_finish() - try: - 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) - if self.selected: - self.label.configure(fg="white", bg="darkblue") - else: - self.label.configure(fg="black", bg="white") - id = self.canvas.create_window(textx, texty, - anchor="nw", window=self.label) - self.label.bind("<1>", self.select_or_edit) - self.label.bind("", self.flip) - self.text_id = id - - def select_or_edit(self, event=None): - if self.selected and self.item.IsEditable(): - self.edit(event) - else: - self.select(event) - - def edit(self, event=None): - self.entry = Entry(self.label, bd=0, highlightthickness=1, width=0) - self.entry.insert(0, self.label['text']) - self.entry.selection_range(0, END) - self.entry.pack(ipadx=5) - self.entry.focus_set() - self.entry.bind("", self.edit_finish) - self.entry.bind("", self.edit_cancel) - - def edit_finish(self, event=None): - try: - entry = self.entry - del self.entry - except AttributeError: - return - text = entry.get() - entry.destroy() - if text and text != self.item.GetText(): - self.item.SetText(text) - text = self.item.GetText() - self.label['text'] = text - self.drawtext() - self.canvas.focus_set() - - def edit_cancel(self, event=None): - self.drawtext() - self.canvas.focus_set() - - -class TreeItem: - - """Abstract class representing tree items. - - Methods should typically be overridden, otherwise a default action - is used. - - """ - - def __init__(self): - """Constructor. Do whatever you need to do.""" - - def GetText(self): - """Return text string to display.""" - - def GetLabelText(self): - """Return label text string to display in front of text (if any).""" - - expandable = None - - def _IsExpandable(self): - """Do not override! Called by TreeNode.""" - if self.expandable is None: - self.expandable = self.IsExpandable() - return self.expandable - - def IsExpandable(self): - """Return whether there are subitems.""" - return 1 - - def _GetSubList(self): - """Do not override! Called by TreeNode.""" - if not self.IsExpandable(): - return [] - sublist = self.GetSubList() - if not sublist: - self.expandable = 0 - return sublist - - def IsEditable(self): - """Return whether the item's text may be edited.""" - - def SetText(self, text): - """Change the item's text (if it is editable).""" - - def GetIconName(self): - """Return name of icon to be displayed normally.""" - - def GetSelectedIconName(self): - """Return name of icon to be displayed when selected.""" - - def GetSubList(self): - """Return list of items forming sublist.""" - - def OnDoubleClick(self): - """Called on a double-click on the item.""" - - -# Example application - -class FileTreeItem(TreeItem): - - """Example TreeItem subclass -- browse the file system.""" - - def __init__(self, path): - self.path = path - - def GetText(self): - return os.path.basename(self.path) or self.path - - def IsEditable(self): - return os.path.basename(self.path) != "" - - def SetText(self, text): - newpath = os.path.dirname(self.path) - newpath = os.path.join(newpath, text) - if os.path.dirname(newpath) != os.path.dirname(self.path): - return - try: - os.rename(self.path, newpath) - self.path = newpath - except os.error: - pass - - def GetIconName(self): - if not self.IsExpandable(): - return "python" # XXX wish there was a "file" icon - - def IsExpandable(self): - return os.path.isdir(self.path) - - def GetSubList(self): - try: - names = os.listdir(self.path) - except os.error: - return [] - names.sort(lambda a, b: cmp(os.path.normcase(a), os.path.normcase(b))) - sublist = [] - for name in names: - item = FileTreeItem(os.path.join(self.path, name)) - sublist.append(item) - return sublist - - -# A canvas widget with scroll bars and some useful bindings - -class ScrolledCanvas: - def __init__(self, master, **opts): - if not opts.has_key('yscrollincrement'): - opts['yscrollincrement'] = 17 - self.master = master - self.frame = Frame(master) - self.frame.rowconfigure(0, weight=1) - self.frame.columnconfigure(0, weight=1) - self.canvas = apply(Canvas, (self.frame,), opts) - self.canvas.grid(row=0, column=0, sticky="nsew") - self.vbar = Scrollbar(self.frame, name="vbar") - self.vbar.grid(row=0, column=1, sticky="nse") - self.hbar = Scrollbar(self.frame, name="hbar", orient="horizontal") - self.hbar.grid(row=1, column=0, sticky="ews") - self.canvas['yscrollcommand'] = self.vbar.set - self.vbar['command'] = self.canvas.yview - self.canvas['xscrollcommand'] = self.hbar.set - self.hbar['command'] = self.canvas.xview - self.canvas.bind("", self.page_up) - self.canvas.bind("", self.page_down) - self.canvas.bind("", self.unit_up) - self.canvas.bind("", self.unit_down) - if isinstance(master, Toplevel) or isinstance(master, Tk): - self.canvas.bind("", self.zoom_height) - self.canvas.focus_set() - def page_up(self, event): - self.canvas.yview_scroll(-1, "page") - return "break" - def page_down(self, event): - self.canvas.yview_scroll(1, "page") - return "break" - def unit_up(self, event): - self.canvas.yview_scroll(-1, "unit") - return "break" - def unit_down(self, event): - self.canvas.yview_scroll(1, "unit") - return "break" - def zoom_height(self, event): - ZoomHeight.zoom_height(self.master) - return "break" - - -# Testing functions - -def test(): - import PyShell - root = Toplevel(PyShell.root) - root.configure(bd=0, bg="yellow") - root.focus_set() - sc = ScrolledCanvas(root, bg="white", highlightthickness=0, takefocus=1) - sc.frame.pack(expand=1, fill="both") - item = FileTreeItem("C:/windows/desktop") - node = TreeNode(sc.canvas, None, item) - node.expand() - -def test2(): - # test w/o scrolling canvas - root = Tk() - root.configure(bd=0) - canvas = Canvas(root, bg="white", highlightthickness=0) - canvas.pack(expand=1, fill="both") - item = FileTreeItem(os.curdir) - node = TreeNode(canvas, None, item) - node.update() - canvas.focus_set() - -if __name__ == '__main__': - test() diff --git a/Lib/idlelib/UndoDelegator.py b/Lib/idlelib/UndoDelegator.py deleted file mode 100644 index ec7af81bcd84..000000000000 --- a/Lib/idlelib/UndoDelegator.py +++ /dev/null @@ -1,352 +0,0 @@ -import sys -import string -from Tkinter import * -from Delegator import Delegator - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - -#$ event <> -#$ win -#$ unix - - -class UndoDelegator(Delegator): - - max_undo = 1000 - - def __init__(self): - Delegator.__init__(self) - self.reset_undo() - - def setdelegate(self, delegate): - if self.delegate is not None: - self.unbind("<>") - self.unbind("<>") - self.unbind("<>") - Delegator.setdelegate(self, delegate) - if delegate is not None: - self.bind("<>", self.undo_event) - self.bind("<>", self.redo_event) - self.bind("<>", self.dump_event) - - def dump_event(self, event): - from pprint import pprint - pprint(self.undolist[:self.pointer]) - print "pointer:", self.pointer, - print "saved:", self.saved, - print "can_merge:", self.can_merge, - print "get_saved():", self.get_saved() - pprint(self.undolist[self.pointer:]) - return "break" - - def reset_undo(self): - self.was_saved = -1 - self.pointer = 0 - self.undolist = [] - self.undoblock = 0 # or a CommandSequence instance - self.set_saved(1) - - def set_saved(self, flag): - if flag: - self.saved = self.pointer - else: - self.saved = -1 - self.can_merge = 0 - self.check_saved() - - def get_saved(self): - return self.saved == self.pointer - - saved_change_hook = None - - def set_saved_change_hook(self, hook): - self.saved_change_hook = hook - - was_saved = -1 - - def check_saved(self): - is_saved = self.get_saved() - if is_saved != self.was_saved: - self.was_saved = is_saved - if self.saved_change_hook: - self.saved_change_hook() - - def insert(self, index, chars, tags=None): - self.addcmd(InsertCommand(index, chars, tags)) - - def delete(self, index1, index2=None): - self.addcmd(DeleteCommand(index1, index2)) - - # Clients should call undo_block_start() and undo_block_stop() - # around a sequence of editing cmds to be treated as a unit by - # undo & redo. Nested matching calls are OK, and the inner calls - # then act like nops. OK too if no editing cmds, or only one - # editing cmd, is issued in between: if no cmds, the whole - # sequence has no effect; and if only one cmd, that cmd is entered - # directly into the undo list, as if undo_block_xxx hadn't been - # called. The intent of all that is to make this scheme easy - # to use: all the client has to worry about is making sure each - # _start() call is matched by a _stop() call. - - def undo_block_start(self): - if self.undoblock == 0: - self.undoblock = CommandSequence() - self.undoblock.bump_depth() - - def undo_block_stop(self): - if self.undoblock.bump_depth(-1) == 0: - cmd = self.undoblock - self.undoblock = 0 - if len(cmd) > 0: - if len(cmd) == 1: - # no need to wrap a single cmd - cmd = cmd.getcmd(0) - # this blk of cmds, or single cmd, has already - # been done, so don't execute it again - self.addcmd(cmd, 0) - - def addcmd(self, cmd, execute=1): - if execute: - cmd.do(self.delegate) - if self.undoblock != 0: - self.undoblock.append(cmd) - return - if self.can_merge and self.pointer > 0: - lastcmd = self.undolist[self.pointer-1] - if lastcmd.merge(cmd): - return - self.undolist[self.pointer:] = [cmd] - if self.saved > self.pointer: - self.saved = -1 - self.pointer = self.pointer + 1 - if len(self.undolist) > self.max_undo: - ##print "truncating undo list" - del self.undolist[0] - self.pointer = self.pointer - 1 - if self.saved >= 0: - self.saved = self.saved - 1 - self.can_merge = 1 - self.check_saved() - - def undo_event(self, event): - if self.pointer == 0: - self.bell() - return "break" - cmd = self.undolist[self.pointer - 1] - cmd.undo(self.delegate) - self.pointer = self.pointer - 1 - self.can_merge = 0 - self.check_saved() - return "break" - - def redo_event(self, event): - if self.pointer >= len(self.undolist): - self.bell() - return "break" - cmd = self.undolist[self.pointer] - cmd.redo(self.delegate) - self.pointer = self.pointer + 1 - self.can_merge = 0 - self.check_saved() - return "break" - - -class Command: - - # Base class for Undoable commands - - tags = None - - def __init__(self, index1, index2, chars, tags=None): - self.marks_before = {} - self.marks_after = {} - self.index1 = index1 - self.index2 = index2 - self.chars = chars - if tags: - self.tags = tags - - def __repr__(self): - s = self.__class__.__name__ - t = (self.index1, self.index2, self.chars, self.tags) - if self.tags is None: - t = t[:-1] - return s + `t` - - def do(self, text): - pass - - def redo(self, text): - pass - - def undo(self, text): - pass - - def merge(self, cmd): - return 0 - - def save_marks(self, text): - marks = {} - for name in text.mark_names(): - if name != "insert" and name != "current": - marks[name] = text.index(name) - return marks - - def set_marks(self, text, marks): - for name, index in marks.items(): - text.mark_set(name, index) - - -class InsertCommand(Command): - - # Undoable insert command - - def __init__(self, index1, chars, tags=None): - Command.__init__(self, index1, None, chars, tags) - - def do(self, text): - self.marks_before = self.save_marks(text) - self.index1 = text.index(self.index1) - if text.compare(self.index1, ">", "end-1c"): - # Insert before the final newline - self.index1 = text.index("end-1c") - text.insert(self.index1, self.chars, self.tags) - self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars))) - self.marks_after = self.save_marks(text) - ##sys.__stderr__.write("do: %s\n" % self) - - def redo(self, text): - text.mark_set('insert', self.index1) - text.insert(self.index1, self.chars, self.tags) - self.set_marks(text, self.marks_after) - text.see('insert') - ##sys.__stderr__.write("redo: %s\n" % self) - - def undo(self, text): - text.mark_set('insert', self.index1) - text.delete(self.index1, self.index2) - self.set_marks(text, self.marks_before) - text.see('insert') - ##sys.__stderr__.write("undo: %s\n" % self) - - def merge(self, cmd): - if self.__class__ is not cmd.__class__: - return 0 - if self.index2 != cmd.index1: - return 0 - if self.tags != cmd.tags: - return 0 - if len(cmd.chars) != 1: - return 0 - if self.chars and \ - self.classify(self.chars[-1]) != self.classify(cmd.chars): - return 0 - self.index2 = cmd.index2 - self.chars = self.chars + cmd.chars - return 1 - - alphanumeric = string.letters + string.digits + "_" - - def classify(self, c): - if c in self.alphanumeric: - return "alphanumeric" - if c == "\n": - return "newline" - return "punctuation" - - -class DeleteCommand(Command): - - # Undoable delete command - - def __init__(self, index1, index2=None): - Command.__init__(self, index1, index2, None, None) - - def do(self, text): - self.marks_before = self.save_marks(text) - self.index1 = text.index(self.index1) - if self.index2: - self.index2 = text.index(self.index2) - else: - self.index2 = text.index(self.index1 + " +1c") - if text.compare(self.index2, ">", "end-1c"): - # Don't delete the final newline - self.index2 = text.index("end-1c") - self.chars = text.get(self.index1, self.index2) - text.delete(self.index1, self.index2) - self.marks_after = self.save_marks(text) - ##sys.__stderr__.write("do: %s\n" % self) - - def redo(self, text): - text.mark_set('insert', self.index1) - text.delete(self.index1, self.index2) - self.set_marks(text, self.marks_after) - text.see('insert') - ##sys.__stderr__.write("redo: %s\n" % self) - - def undo(self, text): - text.mark_set('insert', self.index1) - text.insert(self.index1, self.chars) - self.set_marks(text, self.marks_before) - text.see('insert') - ##sys.__stderr__.write("undo: %s\n" % self) - -class CommandSequence(Command): - - # Wrapper for a sequence of undoable cmds to be undone/redone - # as a unit - - def __init__(self): - self.cmds = [] - self.depth = 0 - - def __repr__(self): - s = self.__class__.__name__ - strs = [] - for cmd in self.cmds: - strs.append(" " + `cmd`) - return s + "(\n" + string.join(strs, ",\n") + "\n)" - - def __len__(self): - return len(self.cmds) - - def append(self, cmd): - self.cmds.append(cmd) - - def getcmd(self, i): - return self.cmds[i] - - def redo(self, text): - for cmd in self.cmds: - cmd.redo(text) - - def undo(self, text): - cmds = self.cmds[:] - cmds.reverse() - for cmd in cmds: - cmd.undo(text) - - def bump_depth(self, incr=1): - self.depth = self.depth + incr - return self.depth - -def main(): - from Percolator import Percolator - root = Tk() - root.wm_protocol("WM_DELETE_WINDOW", root.quit) - text = Text() - text.pack() - text.focus_set() - p = Percolator(text) - d = UndoDelegator() - p.insertfilter(d) - root.mainloop() - -if __name__ == "__main__": - main() diff --git a/Lib/idlelib/WidgetRedirector.py b/Lib/idlelib/WidgetRedirector.py deleted file mode 100644 index b49ccf1c590a..000000000000 --- a/Lib/idlelib/WidgetRedirector.py +++ /dev/null @@ -1,92 +0,0 @@ -from Tkinter import * - - -class WidgetRedirector: - - """Support for redirecting arbitrary widget subcommands.""" - - def __init__(self, widget): - self.dict = {} - self.widget = widget - self.tk = tk = widget.tk - w = widget._w - self.orig = w + "_orig" - tk.call("rename", w, self.orig) - tk.createcommand(w, self.dispatch) - - def __repr__(self): - return "WidgetRedirector(%s<%s>)" % (self.widget.__class__.__name__, - self.widget._w) - - def close(self): - for name in self.dict.keys(): - self.unregister(name) - widget = self.widget; del self.widget - orig = self.orig; del self.orig - tk = widget.tk - w = widget._w - tk.deletecommand(w) - tk.call("rename", orig, w) - - def register(self, name, function): - if self.dict.has_key(name): - previous = dict[name] - else: - previous = OriginalCommand(self, name) - self.dict[name] = function - setattr(self.widget, name, function) - return previous - - def unregister(self, name): - if self.dict.has_key(name): - function = self.dict[name] - del self.dict[name] - if hasattr(self.widget, name): - delattr(self.widget, name) - return function - else: - return None - - def dispatch(self, cmd, *args): - m = self.dict.get(cmd) - try: - if m: - return apply(m, args) - else: - return self.tk.call((self.orig, cmd) + args) - except TclError: - return "" - - -class OriginalCommand: - - def __init__(self, redir, name): - self.redir = redir - self.name = name - self.tk = redir.tk - self.orig = redir.orig - self.tk_call = self.tk.call - self.orig_and_name = (self.orig, self.name) - - def __repr__(self): - return "OriginalCommand(%s, %s)" % (`self.redir`, `self.name`) - - def __call__(self, *args): - return self.tk_call(self.orig_and_name + args) - - -def main(): - root = Tk() - text = Text() - text.pack() - text.focus_set() - redir = WidgetRedirector(text) - global orig_insert - def my_insert(*args): - print "insert", args - apply(orig_insert, args) - orig_insert = redir.register("insert", my_insert) - root.mainloop() - -if __name__ == "__main__": - main() diff --git a/Lib/idlelib/WindowList.py b/Lib/idlelib/WindowList.py deleted file mode 100644 index 7e05a57c0329..000000000000 --- a/Lib/idlelib/WindowList.py +++ /dev/null @@ -1,85 +0,0 @@ -from Tkinter import * - -class WindowList: - - def __init__(self): - self.dict = {} - self.callbacks = [] - - def add(self, window): - window.after_idle(self.call_callbacks) - self.dict[str(window)] = window - - def delete(self, window): - try: - del self.dict[str(window)] - except KeyError: - # Sometimes, destroy() is called twice - pass - self.call_callbacks() - - def add_windows_to_menu(self, menu): - list = [] - for key in self.dict.keys(): - window = self.dict[key] - try: - title = window.get_title() - except TclError: - continue - list.append((title, window)) - list.sort() - for title, window in list: - if title == "Python Shell": - # Hack -- until we have a better way to this - continue - menu.add_command(label=title, command=window.wakeup) - - def register_callback(self, callback): - self.callbacks.append(callback) - - def unregister_callback(self, callback): - try: - self.callbacks.remove(callback) - except ValueError: - pass - - def call_callbacks(self): - for callback in self.callbacks: - try: - callback() - except: - print "warning: callback failed in WindowList", \ - sys.exc_type, ":", sys.exc_value - -registry = WindowList() - -add_windows_to_menu = registry.add_windows_to_menu -register_callback = registry.register_callback -unregister_callback = registry.unregister_callback - - -class ListedToplevel(Toplevel): - - def __init__(self, master, **kw): - Toplevel.__init__(self, master, kw) - registry.add(self) - - def destroy(self): - registry.delete(self) - Toplevel.destroy(self) - - def get_title(self): - # Subclass can override - return self.wm_title() - - def wakeup(self): - try: - if self.wm_state() == "iconic": - self.wm_deiconify() - else: - self.tkraise() - self.focus_set() - except TclError: - # This can happen when the window menu was torn off. - # Simply ignore it. - pass diff --git a/Lib/idlelib/ZoomHeight.py b/Lib/idlelib/ZoomHeight.py deleted file mode 100644 index ecc306a73392..000000000000 --- a/Lib/idlelib/ZoomHeight.py +++ /dev/null @@ -1,46 +0,0 @@ -# Sample extension: zoom a window to maximum height - -import re -import sys - -class ZoomHeight: - - menudefs = [ - ('windows', [ - ('_Zoom Height', '<>'), - ]) - ] - - windows_keydefs = { - '<>': [''], - } - unix_keydefs = { - '<>': [''], - } - - def __init__(self, editwin): - self.editwin = editwin - - def zoom_height_event(self, event): - top = self.editwin.top - zoom_height(top) - -def zoom_height(top): - geom = top.wm_geometry() - m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) - if not m: - top.bell() - return - width, height, x, y = map(int, m.groups()) - newheight = top.winfo_screenheight() - if sys.platform == 'win32': - newy = 0 - newheight = newheight - 72 - else: - newy = 24 - newheight = newheight - 96 - if height >= newheight: - newgeom = "" - else: - newgeom = "%dx%d+%d+%d" % (width, newheight, x, newy) - top.wm_geometry(newgeom) diff --git a/Lib/idlelib/__init__.py b/Lib/idlelib/__init__.py deleted file mode 100644 index 4c5b567c3a29..000000000000 --- a/Lib/idlelib/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Dummy file to make this a potential package. diff --git a/Lib/idlelib/aboutDialog.py b/Lib/idlelib/aboutDialog.py deleted file mode 100644 index 25443ae3609d..000000000000 --- a/Lib/idlelib/aboutDialog.py +++ /dev/null @@ -1,135 +0,0 @@ -##---------------------------------------------------------------------------## -## -## idle - about box -## elguavas -## -##---------------------------------------------------------------------------## -""" -about box for idle -""" -from Tkinter import * -import tkFont -import string, os -import textView -import idlever -class AboutDialog(Toplevel): - """ - modal about dialog for idle - """ - 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="#777777" - self.fg="#ffffff" - #no ugly bold default font on *nix - font=tkFont.Font(self,Label(self).cget('font')) - if os.name=='posix': font.config(weight=NORMAL) - self.textFont=font - - self.CreateWidgets() - self.resizable(height=FALSE,width=FALSE) - self.title(title) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.Ok) - self.parent = parent - self.buttonOk.focus_set() - #key bindings for this dialog - self.bind('',self.CreditsButtonBinding) #credits button - self.bind('',self.LicenseButtonBinding) #license button - self.bind('',self.Ok) #dismiss dialog - self.bind('',self.Ok) #dismiss dialog - self.wait_window() - - 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='Ok', - command=self.Ok)#,default=ACTIVE - 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='IDLEfork',fg=self.fg,bg=self.bg, - 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=0,column=1,sticky=W,rowspan=2,padx=0,pady=3) - labelVersion = Label(frameBg,text='version '+idlever.IDLE_VERSION, - fg=self.fg,bg=self.bg,font=self.textFont) - labelVersion.grid(row=1,column=0,sticky=W,padx=10,pady=5) - labelDesc = Label(frameBg, - text="A development version of Python's lightweight\n"+ - 'Integrated DeveLopment Environment, IDLE.', - justify=LEFT,fg=self.fg,bg=self.bg,font=self.textFont) - labelDesc.grid(row=2,column=0,sticky=W,columnspan=3,padx=10,pady=5) - labelCopyright = Label(frameBg, - text="Copyright (c) 2001 Python Software Foundation;\nAll Rights Reserved", - justify=LEFT,fg=self.fg,bg=self.bg,font=self.textFont) - labelCopyright.grid(row=3,column=0,sticky=W,columnspan=3,padx=10,pady=5) - labelLicense = Label(frameBg, - text='Released under the Python 2.1.1 PSF Licence', - justify=LEFT,fg=self.fg,bg=self.bg,font=self.textFont) - labelLicense.grid(row=4,column=0,sticky=W,columnspan=3,padx=10,pady=5) - framePad = Frame(frameBg,height=5,bg=self.bg).grid(row=5,column=0) - labelEmail = Label(frameBg,text='email: idle-dev@python.org', - justify=LEFT,fg=self.fg,bg=self.bg,font=self.textFont) - labelEmail.grid(row=6,column=0,columnspan=2,sticky=W,padx=10,pady=0) - labelWWW = Label(frameBg,text='www: http://idlefork.sourceforge.net', - justify=LEFT,fg=self.fg,bg=self.bg,font=self.textFont) - labelWWW.grid(row=7,column=0,columnspan=2,sticky=W,padx=10,pady=0) - frameDivider = Frame(frameBg,borderwidth=1,relief=SUNKEN, - height=2,bg=self.bg).grid(row=8,column=0,sticky=(E,W),columnspan=3, - padx=5,pady=5) - labelPythonVer = Label(frameBg,text='Python version: '+ - sys.version.split()[0],fg=self.fg,bg=self.bg,font=self.textFont) - labelPythonVer.grid(row=9,column=0,sticky=W,padx=10,pady=0) - #handle weird tk version num in windoze python >= 1.6 (?!?) - tkVer = `TkVersion`.split('.') - tkVer[len(tkVer)-1] = str('%.3g' % (float('.'+tkVer[len(tkVer)-1])))[2:] - if tkVer[len(tkVer)-1] == '': - tkVer[len(tkVer)-1] = '0' - tkVer = string.join(tkVer,'.') - labelTkVer = Label(frameBg,text='Tk version: '+tkVer,fg=self.fg,bg=self.bg, - font=self.textFont) - labelTkVer.grid(row=9,column=1,sticky=W,padx=2,pady=0) - - self.buttonLicense = Button(frameBg,text='View License',underline=5, - width=14,highlightbackground=self.bg,command=self.ShowLicense)#takefocus=FALSE - self.buttonLicense.grid(row=10,column=0,sticky=W,padx=10,pady=10) - self.buttonCredits = Button(frameBg,text='View Credits',underline=5, - width=14,highlightbackground=self.bg,command=self.ShowCredits)#takefocus=FALSE - self.buttonCredits.grid(row=10,column=1,columnspan=2,sticky=E,padx=10,pady=10) - - def CreditsButtonBinding(self,event): - self.buttonCredits.invoke() - - def LicenseButtonBinding(self,event): - self.buttonLicense.invoke() - - def ShowLicense(self): - self.ViewFile('About - License','LICENSE.txt') - - def ShowCredits(self): - self.ViewFile('About - Credits','CREDITS.txt') - - def ViewFile(self,viewTitle,viewFile): - fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),viewFile) - textView.TextViewer(self,viewTitle,fn) - - def Ok(self, event=None): - self.destroy() - -if __name__ == '__main__': - #test the dialog - root=Tk() - def run(): - import aboutDialog - aboutDialog.AboutDialog(root,'About') - Button(root,text='Dialog',command=run).pack() - root.mainloop() diff --git a/Lib/idlelib/config-extensions.def b/Lib/idlelib/config-extensions.def deleted file mode 100644 index d1c1ee237478..000000000000 --- a/Lib/idlelib/config-extensions.def +++ /dev/null @@ -1,34 +0,0 @@ -# IDLE reads several config files to determine user preferences. This -# file is the default config file for idle extensions settings. - -[SearchBinding] -enable=1 - -[AutoIndent] -enable=1 - -[AutoExpand] -enable=1 - -[FormatParagraph] -enable=1 - -[ZoomHeight] -enable=1 - -#[ScriptBinding] # disabled in favor of ExecBinding -#enable=0 - -[ExecBinding] -enable=1 - -[CallTips] -enable=1 - -[ParenMatch] -enable=0 -style= expression -flash-delay= 500 -bell= 1 -hilite-foreground= black -hilite-background= #43cd80 diff --git a/Lib/idlelib/config-highlight.def b/Lib/idlelib/config-highlight.def deleted file mode 100644 index 83c9807a8c08..000000000000 --- a/Lib/idlelib/config-highlight.def +++ /dev/null @@ -1,56 +0,0 @@ -# IDLE reads several config files to determine user preferences. This -# file is the default config file for idle highlight theme settings. - -[IDLE Classic Old - plain fonts] -normal-foreground= black -normal-background= white -normal-fontStyle= normal -keyword-foreground= #ff7700 -keyword-fontStyle= normal -comment-foreground= #dd0000 -comment-fontStyle= normal -string-foreground= #00aa00 -string-fontStyle= normal -definition-foreground= #0000ff -definition-fontStyle= normal -hilite-foreground= #000068 -hilite-background= #006868 -hilite-fontStyle= normal -break-foreground= #ff7777 -break-fontStyle= normal -hit-background= #000000 -hit-foreground= #ffffff -hit-fontStyle= normal -cursor-foreround= black -error-background= #ff7777 -#shell window -stdout-foreground= blue -stdout-fontStyle= normal -stderr-foreground= red -stderr-fontStyle= normal -console-foreground= #770000 -console-fontStyle= normal - -[IDLE Classic New] -normal-foreground= black -normal-background= white -normal-fontStyle= normal -keyword-foreground= #ff7700 -keyword-fontStyle= bold -comment-foreground= #dd0000 -comment-fontStyle= italic -string-foreground= #00aa00 -string-fontStyle= normal -definition-foreground= #0000ff -definition-fontStyle= bold -hilite-foreground= #000068 -hilite-background= #006868 -break-foreground= #ff7777 -hit-background= #000000 -hit-foreground= #ffffff -cursor-foreground= black -error-background= #ff7777 -#shell window -stdout-foreground= blue -stderr-foreground= red -console-foreground= #770000 diff --git a/Lib/idlelib/config-keys.def b/Lib/idlelib/config-keys.def deleted file mode 100644 index feec31c538d1..000000000000 --- a/Lib/idlelib/config-keys.def +++ /dev/null @@ -1,64 +0,0 @@ -# IDLE reads several config files to determine user preferences. This -# file is the default config file for idle key binding settings. -# Where multiple keys are specified for an action: if they are separated -# by a space (eg. action= ) then the keys are altenatives, if -# there is no space (eg. action=key2>) then the keys comprise a -# single 'emacs style' multi-keystoke binding. - -[IDLE CUA-ish] -Copy= -Cut= -Paste= -beginning-of-line= -center-insert= -close-all-windows= -close-window= -dump-undo-state= -end-of-file= -python-docs= -python-context-help= -history-next= -history-previous= -interrupt-execution= -open-class-browser= -open-module= -open-new-window= -open-window-from-file= -plain-newline-and-indent= -redo= -remove-selection= -save-copy-of-window-as-file= -save-window-as-file= -save-window= -select-all= -toggle-auto-coloring= -undo= - -[IDLE Emacs-ish] -Copy= -Cut= -Paste= -beginning-of-line= -center-insert= -close-all-windows= -close-window= -do-nothing= -dump-undo-state= -end-of-file= -history-next= -history-previous= -interrupt-execution= -open-class-browser= -open-module= -open-new-window= -open-window-from-file= -plain-newline-and-indent= -python-docs= -python-context-help= -redo= -save-copy-of-window-as-file= -save-window-as-file= -save-window= -select-all= -toggle-auto-coloring= -undo= diff --git a/Lib/idlelib/config-main.def b/Lib/idlelib/config-main.def deleted file mode 100644 index dca0e543122f..000000000000 --- a/Lib/idlelib/config-main.def +++ /dev/null @@ -1,79 +0,0 @@ -# IDLE reads several config files to determine user preferences. This -# file is the default config file for general idle settings. -# -# When IDLE starts, it will look in -# the following two sets of files, in order: -# -# default configuration -# --------------------- -# config-main.def the default general config file -# config-extensions.def the default extension config file -# config-highlight.def the default highlighting config file -# config-keys.def the default keybinding config file -# -# user configuration -# ------------------- -# ~/.idlerc/idle-main.cfg the user general config file -# ~/.idlerc/idle-extensions.cfg the user extension config file -# ~/.idlerc/idle-highlight.cfg the user highlighting config file -# ~/.idlerc/idle-keys.cfg the user keybinding config file -# -# Any options the user saves through the config dialog will be saved to -# the relevant user config file. Reverting any general setting to the -# default causes that entry to be wiped from the user file and re-read -# from the default file. User highlighting themes or keybinding sets are -# retained unless specifically deleted within the config dialog. Choosing -# one of the default themes or keysets just applies the relevant settings -# from the default file. - -[General] -run-in-separate-process= 1 -help-browser= "" - -[HelpFiles] -idle="IDLE _Help","" -python="_Python Documentation","" -#additional help sources -1= -2= -3= -4= -5= -6= -7= -8= -9= -10= - -[EditorWindow] -editor-on-startup= 0 -width= 80 -height= 24 -font= courier -font-size= 12 - -[Indent] -use-spaces= 1 -num-spaces= 4 -tab-cols= 4 - -[Theme] -default= 1 -name= IDLE Classic New - -[Keys] -default= 1 -name= IDLE Classic - windows - -[RecentFiles] -1= -2= -3= -4= -5= -6= -7= -8= -9= -10= - diff --git a/Lib/idlelib/config-unix.txt b/Lib/idlelib/config-unix.txt deleted file mode 100644 index be9fa814ce02..000000000000 --- a/Lib/idlelib/config-unix.txt +++ /dev/null @@ -1,3 +0,0 @@ -[EditorWindow] -font-name= courier -font-size= 10 diff --git a/Lib/idlelib/config-win.txt b/Lib/idlelib/config-win.txt deleted file mode 100644 index 9faa6353035e..000000000000 --- a/Lib/idlelib/config-win.txt +++ /dev/null @@ -1,3 +0,0 @@ -[EditorWindow] -font-name: courier new -font-size: 10 diff --git a/Lib/idlelib/config.txt b/Lib/idlelib/config.txt deleted file mode 100644 index 223b302ca3d1..000000000000 --- a/Lib/idlelib/config.txt +++ /dev/null @@ -1,66 +0,0 @@ -# IDLE reads several config files to determine user preferences. This -# file is the default config file. When IDLE starts, it will look in -# the following four files in order: -# config.txt the default config file -# config-[win/unix/mac].txt the generic platform config file -# config-[sys.platform].txt the specific platform config file -# ~/.idle the user config file -# XXX what about Windows? -# -# The last definition of each option is used. For example, you can -# override the default window size (80x24) by defining width and -# height options in the EditorWindow section of your ~/.idle file -# -# IDLE extensions can be enabled and disabled by adding them to one of -# the config files. To enable an extension, create a section with the -# same name as the extension, e.g. the [ParenMatch] section below. To -# disable an extension, either remove the section or add the 'enable' -# option with the value 0. - -[EditorWindow] -width= 80 -height= 24 -# fonts defined in config-[win/unix].txt - -[Colors] -normal-foreground= black -normal-background= white -# These color types are not explicitly defined= sync, todo, stdin -keyword-foreground= #ff7700 -comment-foreground= #dd0000 -string-foreground= #00aa00 -definition-foreground= #0000ff -hilite-foreground= #000068 -hilite-background= #006868 -break-foreground= #ff7777 -hit-foreground= #ffffff -hit-background= #000000 -stdout-foreground= blue -stderr-foreground= red -console-foreground= #770000 -error-background= #ff7777 -cursor-background= black - -[SearchBinding] - -[AutoIndent] - -[AutoExpand] - -[FormatParagraph] - -[ZoomHeight] - -#[ScriptBinding] # disabled in favor of ExecBinding - -[ExecBinding] - -[CallTips] - -[ParenMatch] -enable= 0 -style= expression -flash-delay= 500 -bell= 1 -hilite-foreground= black -hilite-background= #43cd80 diff --git a/Lib/idlelib/configDialog.py b/Lib/idlelib/configDialog.py deleted file mode 100644 index b131eeb43f55..000000000000 --- a/Lib/idlelib/configDialog.py +++ /dev/null @@ -1,623 +0,0 @@ -""" -configuration dialog -""" -from Tkinter import * -import tkMessageBox, tkColorChooser, tkFont - -from configHandler import idleConf -from dynOptionMenuWidget import DynOptionMenu -from tabpage import TabPageSet - -class ConfigDialog(Toplevel): - """ - configuration dialog for idle - """ - def __init__(self,parent,title): - Toplevel.__init__(self, parent) - self.configure(borderwidth=5) - self.geometry("+%d+%d" % (parent.winfo_rootx()+20, - parent.winfo_rooty()+30)) - #Theme Elements. Each theme element key is it's display name. - #The first value of the tuple is the sample area tag name. - #The second value is the display name list sort index. - #The third value indicates whether the element can have a foreground - #or background colour or both. - self.themeElements={'Normal Text':('normal','00','both'), - 'Python Keywords':('keyword','01','both'), - 'Python Definitions':('definition','02','both'), - 'Python Comments':('comment','03','both'), - 'Python Strings':('string','04','both'), - 'Selected Text':('hilite','05','both'), - 'Found Text':('hit','06','both'), - 'Cursor':('cursor','07','fg'), - 'Error Background':('error','08','bg'), - 'Shell Foreground':('console','09','fg'), - 'Shell Stdout Foreground':('stdout','10','fg'), - 'Shell Stderr Foreground':('stderr','11','fg')} - self.CreateWidgets() - self.resizable(height=FALSE,width=FALSE) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.Cancel) - self.parent = parent - self.tabPages.focus_set() - #key bindings for this dialog - self.bind('',self.CancelBinding) #dismiss dialog, no save - self.bind('',self.ApplyBinding) #apply changes, save - self.bind('',self.HelpBinding) #context help - self.LoadConfigs() - self.wait_window() - - def Cancel(self): - self.destroy() - - def Ok(self): - pass - - def Apply(self): - pass - - def Help(self): - pass - - def CancelBinding(self,event): - self.Cancel() - - def OkBinding(self,event): - self.Ok() - - def ApplyBinding(self,event): - self.Apply() - - def HelpBinding(self,event): - self.Help() - - def SetThemeType(self): - if self.themeIsBuiltin.get(): - self.optMenuThemeBuiltin.config(state=NORMAL) - self.optMenuThemeCustom.config(state=DISABLED) - self.buttonDeleteCustomTheme.config(state=DISABLED) - else: - self.optMenuThemeBuiltin.config(state=DISABLED) - self.optMenuThemeCustom.config(state=NORMAL) - self.buttonDeleteCustomTheme.config(state=NORMAL) - - def SetKeysType(self): - if self.keysAreDefault.get(): - self.optMenuKeysBuiltin.config(state=NORMAL) - self.optMenuKeysCustom.config(state=DISABLED) - self.buttonDeleteCustomKeys.config(state=DISABLED) - else: - self.optMenuKeysBuiltin.config(state=DISABLED) - self.optMenuKeysCustom.config(state=NORMAL) - self.buttonDeleteCustomKeys.config(state=NORMAL) - - def GetColour(self): - target=self.highlightTarget.get() - rgbTuplet, colourString = tkColorChooser.askcolor(parent=self, - title='Pick new colour for : '+target, - initialcolor=self.frameColourSet.cget('bg')) - if colourString: #user didn't cancel - self.frameColourSet.config(bg=colourString)#set sample - if self.fgHilite.get(): plane='foreground' - else: plane='background' - apply(self.textHighlightSample.tag_config, - (self.themeElements[target][0],),{plane:colourString}) - - def SetFontSampleBinding(self,event): - self.SetFontSample() - - def SetFontSample(self): - self.editFont.config(size=self.fontSize.get(),weight=NORMAL, - family=self.listFontName.get(self.listFontName.curselection()[0])) - - def SetHighlightTargetBinding(self,*args): - self.SetHighlightTarget() - - def SetHighlightTarget(self): - colourPlane=self.themeElements[self.highlightTarget.get()][2] - if colourPlane == 'bg': - self.radioFg.config(state=DISABLED) - self.radioBg.config(state=DISABLED) - self.fgHilite.set(0) - elif colourPlane == 'fg': - self.radioFg.config(state=DISABLED) - self.radioBg.config(state=DISABLED) - self.fgHilite.set(1) - elif colourPlane == 'both': - self.radioFg.config(state=NORMAL) - self.radioBg.config(state=NORMAL) - self.fgHilite.set(1) #default to setting foreground attribute - 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) - - def CreateWidgets(self): - self.tabPages = TabPageSet(self, - pageNames=['Fonts/Tabs','Highlighting','Keys','General']) - frameActionButtons = Frame(self) - #action buttons - self.buttonHelp = Button(frameActionButtons,text='Help', - 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,underline=0,takefocus=FALSE) - self.buttonCancel = Button(frameActionButtons,text='Cancel', - command=self.Cancel,takefocus=FALSE) - self.CreatePageFontTab() - self.CreatePageHighlight() - self.CreatePageKeys() - self.CreatePageGeneral() - self.buttonHelp.pack(side=RIGHT,padx=5,pady=5) - self.buttonOk.pack(side=LEFT,padx=5,pady=5) - self.buttonApply.pack(side=LEFT,padx=5,pady=5) - self.buttonCancel.pack(side=LEFT,padx=5,pady=5) - frameActionButtons.pack(side=BOTTOM) - self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH) - - - def CreatePageFontTab(self): - #tkVars - self.fontSize=StringVar(self) - self.fontBold=StringVar(self) - self.spaceNum=IntVar(self) - self.tabCols=IntVar(self) - self.indentType=IntVar(self) - self.editFont=tkFont.Font(self,('courier',12,'normal')) - ##widget creation - #body frame - frame=self.tabPages.pages['Fonts/Tabs']['page'] - #body section frames - frameFont=Frame(frame,borderwidth=2,relief=GROOVE) - frameIndent=Frame(frame,borderwidth=2,relief=GROOVE) - #frameFont - labelFontTitle=Label(frameFont,text='Set Base Editor Font') - frameFontName=Frame(frameFont) - frameFontParam=Frame(frameFont) - labelFontNameTitle=Label(frameFontName,justify=LEFT, - text='Font :') - self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE, - exportselection=FALSE) - self.listFontName.bind('<>',self.SetFontSampleBinding) - scrollFont=Scrollbar(frameFontName) - scrollFont.config(command=self.listFontName.yview) - self.listFontName.config(yscrollcommand=scrollFont.set) - labelFontSizeTitle=Label(frameFontParam,text='Size :') - self.optMenuFontSize=DynOptionMenu(frameFontParam,self.fontSize,None, - command=self.SetFontSampleBinding) - checkFontBold=Checkbutton(frameFontParam,variable=self.fontBold, - onvalue='Bold',offvalue='',text='Bold') - frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1) - self.labelFontSample=Label(frameFontSample, - text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]', - justify=LEFT,font=self.editFont) - #frameIndent - labelIndentTitle=Label(frameIndent,text='Set Indentation Defaults') - frameIndentType=Frame(frameIndent) - frameIndentSize=Frame(frameIndent) - labelIndentTypeTitle=Label(frameIndentType, - text='Choose indentation type :') - radioUseSpaces=Radiobutton(frameIndentType,variable=self.indentType, - value=1,text='Tab key inserts spaces') - radioUseTabs=Radiobutton(frameIndentType,variable=self.indentType, - value=0,text='Tab key inserts tabs') - labelIndentSizeTitle=Label(frameIndentSize, - text='Choose indentation size :') - labelSpaceNumTitle=Label(frameIndentSize,justify=LEFT, - text='when tab key inserts spaces,\nspaces per tab') - self.scaleSpaceNum=Scale(frameIndentSize,variable=self.spaceNum, - orient='horizontal',tickinterval=2,from_=2,to=8) - labeltabColsTitle=Label(frameIndentSize,justify=LEFT, - text='when tab key inserts tabs,\ncolumns per tab') - self.scaleTabCols=Scale(frameIndentSize,variable=self.tabCols, - orient='horizontal',tickinterval=2,from_=2,to=8) - #widget packing - #body - frameFont.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH) - frameIndent.pack(side=LEFT,padx=5,pady=10,fill=Y) - #frameFont - labelFontTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - frameFontName.pack(side=TOP,padx=5,pady=5,fill=X) - frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X) - labelFontNameTitle.pack(side=TOP,anchor=W) - 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) - #frameIndent - labelIndentTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - frameIndentType.pack(side=TOP,padx=5,fill=X) - frameIndentSize.pack(side=TOP,padx=5,pady=5,fill=BOTH) - labelIndentTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - radioUseSpaces.pack(side=TOP,anchor=W,padx=5) - radioUseTabs.pack(side=TOP,anchor=W,padx=5) - labelIndentSizeTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5) - self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X) - labeltabColsTitle.pack(side=TOP,anchor=W,padx=5) - self.scaleTabCols.pack(side=TOP,padx=5,fill=X) - return frame - - def CreatePageHighlight(self): - self.builtinTheme=StringVar(self) - self.customTheme=StringVar(self) - self.fgHilite=IntVar(self) - self.colour=StringVar(self) - self.fontName=StringVar(self) - self.themeIsBuiltin=IntVar(self) - self.highlightTarget=StringVar(self) - self.highlightTarget.trace_variable('w',self.SetHighlightTargetBinding) - ##widget creation - #body frame - frame=self.tabPages.pages['Highlighting']['page'] - #body section frames - frameCustom=Frame(frame,borderwidth=2,relief=GROOVE) - frameTheme=Frame(frame,borderwidth=2,relief=GROOVE) - #frameCustom - self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1, - font=('courier',12,''),cursor='hand2',width=10,height=10, - takefocus=FALSE,highlightthickness=0) - text=self.textHighlightSample - text.bind('',lambda e: 'break') - text.bind('',lambda e: 'break') - textAndTags=(('#you can click in 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\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]) - 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.config(state=DISABLED) - self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1) - frameFgBg=Frame(frameCustom) - labelCustomTitle=Label(frameCustom,text='Set Custom Highlighting') - buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :', - command=self.GetColour,highlightthickness=0) - self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet, - 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.fgHilite.set(1) - buttonSaveCustomTheme=Button(frameCustom, - text='Save as a Custom Theme') - #frameTheme - labelThemeTitle=Label(frameTheme,text='Select a Highlighting Theme') - labelTypeTitle=Label(frameTheme,text='Select : ') - self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin, - value=1,command=self.SetThemeType,text='a Built-in Theme') - 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') - ##widget packing - #body - frameCustom.pack(side=LEFT,padx=5,pady=10,expand=TRUE,fill=BOTH) - frameTheme.pack(side=LEFT,padx=5,pady=10,fill=Y) - #frameCustom - labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X) - frameFgBg.pack(side=TOP,padx=5,pady=0) - self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE, - 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) - #frameTheme - labelThemeTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5) - self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2) - 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): - #tkVars - self.bindingTarget=StringVar(self) - self.builtinKeys=StringVar(self) - self.customKeys=StringVar(self) - self.keyChars=StringVar(self) - self.keyCtrl=StringVar(self) - self.keyAlt=StringVar(self) - self.keyShift=StringVar(self) - self.keysAreDefault=IntVar(self) - ##widget creation - #body frame - frame=self.tabPages.pages['Keys']['page'] - #body section frames - frameCustom=Frame(frame,borderwidth=2,relief=GROOVE) - frameKeySets=Frame(frame,borderwidth=2,relief=GROOVE) - #frameCustom - frameTarget=Frame(frameCustom) - frameSet=Frame(frameCustom) - labelCustomTitle=Label(frameCustom,text='Set Custom Key Bindings') - labelTargetTitle=Label(frameTarget,text='Action') - scrollTarget=Scrollbar(frameTarget) - listTarget=Listbox(frameTarget) - scrollTarget.config(command=listTarget.yview) - listTarget.config(yscrollcommand=scrollTarget.set) - labelKeyBindTitle=Label(frameSet,text='Binding') - labelModifierTitle=Label(frameSet,text='Modifier:') - checkCtrl=Checkbutton(frameSet,text='Ctrl') - checkAlt=Checkbutton(frameSet,text='Alt') - checkShift=Checkbutton(frameSet,text='Shift') - labelKeyEntryTitle=Label(frameSet,text='Key:') - entryKey=Entry(frameSet,width=4) - buttonSaveCustomKeys=Button(frameCustom,text='Save as a Custom Key Set') - #frameKeySets - labelKeysTitle=Label(frameKeySets,text='Select a Key Set') - labelTypeTitle=Label(frameKeySets,text='Select : ') - self.radioKeysBuiltin=Radiobutton(frameKeySets,variable=self.keysAreDefault, - value=1,command=self.SetKeysType,text='a Built-in Key Set') - self.radioKeysCustom=Radiobutton(frameKeySets,variable=self.keysAreDefault, - value=0,command=self.SetKeysType,text='a Custom Key Set') - self.optMenuKeysBuiltin=DynOptionMenu(frameKeySets, - self.builtinKeys,None,command=None) - self.optMenuKeysCustom=DynOptionMenu(frameKeySets, - self.customKeys,None,command=None) - self.buttonDeleteCustomKeys=Button(frameKeySets,text='Delete Custom Key Set') - ##widget packing - #body - frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH) - frameKeySets.pack(side=LEFT,padx=5,pady=5,fill=Y) - #frameCustom - labelCustomTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - buttonSaveCustomKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5) - frameTarget.pack(side=LEFT,padx=5,pady=5,fill=Y) - frameSet.pack(side=LEFT,padx=5,pady=5,fill=Y) - labelTargetTitle.pack(side=TOP,anchor=W) - scrollTarget.pack(side=RIGHT,anchor=W,fill=Y) - listTarget.pack(side=TOP,anchor=W,expand=TRUE,fill=BOTH) - labelKeyBindTitle.pack(side=TOP,anchor=W) - labelModifierTitle.pack(side=TOP,anchor=W,pady=5) - checkCtrl.pack(side=TOP,anchor=W) - checkAlt.pack(side=TOP,anchor=W,pady=2) - checkShift.pack(side=TOP,anchor=W) - labelKeyEntryTitle.pack(side=TOP,anchor=W,pady=5) - entryKey.pack(side=TOP,anchor=W) - #frameKeySets - labelKeysTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - self.radioKeysBuiltin.pack(side=TOP,anchor=W,padx=5) - self.radioKeysCustom.pack(side=TOP,anchor=W,padx=5,pady=2) - self.optMenuKeysBuiltin.pack(side=TOP,fill=X,padx=5,pady=5) - self.optMenuKeysCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5) - self.buttonDeleteCustomKeys.pack(side=TOP,fill=X,padx=5,pady=5) - return frame - - def CreatePageGeneral(self): - #tkVars - self.runType=IntVar(self) - self.winWidth=StringVar(self) - self.winHeight=StringVar(self) - self.extState=IntVar(self) - #widget creation - #body - frame=self.tabPages.pages['General']['page'] - #body section frames - frameRun=Frame(frame,borderwidth=2,relief=GROOVE) - frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE) - frameExt=Frame(frame,borderwidth=2,relief=GROOVE) - #frameRun - labelRunTitle=Label(frameRun,text='Run Preferences') - labelRunChoiceTitle=Label(frameRun,text='Run code : ') - radioRunInternal=Radiobutton(frameRun,variable=self.runType, - value=0,command=self.SetKeysType,text="in IDLE's Process") - radioRunSeparate=Radiobutton(frameRun,variable=self.runType, - value=1,command=self.SetKeysType,text='in a Separate Process') - #frameWinSize - labelWinSizeTitle=Label(frameWinSize,text='Initial Window Size') - buttonWinSizeSet=Button(frameWinSize,text='Set to current window size') - labelWinWidthTitle=Label(frameWinSize,text='Width') - entryWinWidth=Entry(frameWinSize,textvariable=self.winWidth, - width=3) - labelWinHeightTitle=Label(frameWinSize,text='Height') - entryWinHeight=Entry(frameWinSize,textvariable=self.winHeight, - width=3) - #frameExt - frameExtList=Frame(frameExt) - frameExtSet=Frame(frameExt) - labelExtTitle=Label(frameExt,text='Configure IDLE Extensions') - labelExtListTitle=Label(frameExtList,text='Extension') - scrollExtList=Scrollbar(frameExtList) - listExt=Listbox(frameExtList,height=5) - scrollExtList.config(command=listExt.yview) - listExt.config(yscrollcommand=scrollExtList.set) - labelExtSetTitle=Label(frameExtSet,text='Settings') - radioEnableExt=Radiobutton(frameExtSet,variable=self.extState, - value=1,text="enable") - radioDisableExt=Radiobutton(frameExtSet,variable=self.extState, - value=0,text="disable") - self.extState.set(1) - buttonExtConfig=Button(frameExtSet,text='Configure') - - #widget packing - #body - frameRun.pack(side=TOP,padx=5,pady=5,fill=X) - frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X) - frameExt.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH) - #frameRun - labelRunTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) - radioRunInternal.pack(side=LEFT,anchor=W,padx=5,pady=5) - radioRunSeparate.pack(side=LEFT,anchor=W,padx=5,pady=5) - #frameWinSize - labelWinSizeTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - buttonWinSizeSet.pack(side=LEFT,anchor=W,padx=5,pady=5) - labelWinWidthTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) - entryWinWidth.pack(side=LEFT,anchor=W,padx=5,pady=5) - labelWinHeightTitle.pack(side=LEFT,anchor=W,padx=5,pady=5) - entryWinHeight.pack(side=LEFT,anchor=W,padx=5,pady=5) - #frameExt - labelExtTitle.pack(side=TOP,anchor=W,padx=5,pady=5) - frameExtSet.pack(side=RIGHT,padx=5,pady=5,fill=Y) - frameExtList.pack(side=RIGHT,padx=5,pady=5,expand=TRUE,fill=BOTH) - labelExtListTitle.pack(side=TOP,anchor=W) - scrollExtList.pack(side=RIGHT,anchor=W,fill=Y) - listExt.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH) - labelExtSetTitle.pack(side=TOP,anchor=W) - radioEnableExt.pack(side=TOP,anchor=W) - radioDisableExt.pack(side=TOP,anchor=W) - buttonExtConfig.pack(side=TOP,anchor=W,pady=5) - - return frame - - def PaintThemeSample(self): - if self.themeIsBuiltin.get(): #a default theme - theme=self.builtinTheme.get() - else: #a user theme - theme=self.customTheme.get() - for element in self.themeElements.keys(): - colours=idleConf.GetHighlight(theme, self.themeElements[element][0]) - apply(self.textHighlightSample.tag_config, - (self.themeElements[element][0],),colours) - - def LoadFontCfg(self): - ##base editor font selection list - fonts=list(tkFont.families(self)) - fonts.sort() - for font in fonts: - self.listFontName.insert(END,font) - configuredFont=idleConf.GetOption('main','EditorWindow','font', - default='courier') - if configuredFont in fonts: - currentFontIndex=fonts.index(configuredFont) - self.listFontName.see(currentFontIndex) - self.listFontName.select_set(currentFontIndex) - ##font size dropdown - fontSize=idleConf.GetOption('main','EditorWindow','font-size',default='12') - self.optMenuFontSize.SetMenu(('10','11','12','13','14', - '16','18','20','22'),fontSize ) - ##font sample - self.SetFontSample() - - def LoadTabCfg(self): - ##indent type radibuttons - spaceIndent=idleConf.GetOption('main','Indent','use-spaces', - default=1,type='bool') - self.indentType.set(spaceIndent) - ##indent sizes - spaceNum=idleConf.GetOption('main','Indent','num-spaces', - default=4,type='int') - tabCols=idleConf.GetOption('main','Indent','tab-cols', - default=4,type='int') - self.spaceNum.set(spaceNum) - self.tabCols.set(tabCols) - - def LoadThemeCfg(self): - ##current theme type radiobutton - self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default', - type='int',default=1)) - ##currently set theme - currentOption=idleConf.GetOption('main','Theme','name') - ##load available theme option menus - if self.themeIsBuiltin.get(): #default theme selected - itemList=idleConf.GetSectionList('default','highlight') - self.optMenuThemeBuiltin.SetMenu(itemList,currentOption) - itemList=idleConf.GetSectionList('user','highlight') - if not itemList: - self.radioThemeCustom.config(state=DISABLED) - self.customTheme.set('- no custom themes -') - else: - self.optMenuThemeCustom.SetMenu(itemList,itemList[0]) - else: #user theme selected - itemList=idleConf.GetSectionList('user','highlight') - self.optMenuThemeCustom.SetMenu(itemList,currentOption) - itemList=idleConf.GetSectionList('default','highlight') - self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0]) - self.SetThemeType() - ##load theme element option menu - themeNames=self.themeElements.keys() - themeNames.sort(self.__ThemeNameIndexCompare) - self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0]) - sampleBg=idleConf.GetHighlight(currentOption, - self.highlightTarget.get())['background'] - self.fgHilite.set(0) - self.frameColourSet.config(bg=sampleBg) - self.PaintThemeSample() - - def __ThemeNameIndexCompare(self,a,b): - if self.themeElements[a][1]>': ['', ''], - '<>': ['', ''], - '<>': ['', ''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': ['']} - if keySetName: - pass - - return keyBindings - - - def LoadCfgFiles(self): - """ - load all configuration files. - """ - for key in self.defaultCfg.keys(): - self.defaultCfg[key].Load() - self.userCfg[key].Load() #same keys - - def SaveUserCfgFiles(self): - """ - write all loaded user configuration files back to disk - """ - for key in self.userCfg.keys(): - self.userCfg[key].Save() - -idleConf=IdleConf() - -### module test -if __name__ == '__main__': - def dumpCfg(cfg): - print '\n',cfg,'\n' - for key in cfg.keys(): - sections=cfg[key].sections() - print key - print sections - for section in sections: - options=cfg[key].options(section) - print section - print options - for option in options: - print option, '=', cfg[key].Get(section,option) - dumpCfg(idleConf.defaultCfg) - dumpCfg(idleConf.userCfg) - print idleConf.userCfg['main'].Get('Theme','name') - #print idleConf.userCfg['highlight'].GetDefHighlight('Foo','normal') diff --git a/Lib/idlelib/dynOptionMenuWidget.py b/Lib/idlelib/dynOptionMenuWidget.py deleted file mode 100644 index bc716ed2727e..000000000000 --- a/Lib/idlelib/dynOptionMenuWidget.py +++ /dev/null @@ -1,41 +0,0 @@ -##---------------------------------------------------------------------------## -## -## idle - modified OptionMenu widget -## elguavas -## -##---------------------------------------------------------------------------## -""" -OptionMenu widget modified to allow dynamic menu reconfiguration -and setting of highlightthickness -""" -from Tkinter import OptionMenu -from Tkinter import _setit -import copy - -class DynOptionMenu(OptionMenu): - """ - 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']) - 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): - """ - 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, - command=_setit(self.variable,item,self.command)) - if value: - self.variable.set(value) diff --git a/Lib/idlelib/eventparse.py b/Lib/idlelib/eventparse.py deleted file mode 100644 index cb2028dc0370..000000000000 --- a/Lib/idlelib/eventparse.py +++ /dev/null @@ -1,93 +0,0 @@ -#! /usr/bin/env python - -"""Parse event definitions out of comments in source files.""" - -import re -import sys -import os -import string -import getopt -import glob -import fileinput -import pprint - -def main(): - hits = [] - sublist = [] - args = sys.argv[1:] - if not args: - args = filter(lambda s: 'A' <= s[0] <= 'Z', glob.glob("*.py")) - if not args: - print "No arguments, no [A-Z]*.py files." - return 1 - for line in fileinput.input(args): - if line[:2] == '#$': - if not sublist: - sublist.append('file %s' % fileinput.filename()) - sublist.append('line %d' % fileinput.lineno()) - sublist.append(string.strip(line[2:-1])) - else: - if sublist: - hits.append(sublist) - sublist = [] - if sublist: - hits.append(sublist) - sublist = [] - dd = {} - for sublist in hits: - d = {} - for line in sublist: - words = string.split(line, None, 1) - if len(words) != 2: - continue - tag = words[0] - l = d.get(tag, []) - l.append(words[1]) - d[tag] = l - if d.has_key('event'): - keys = d['event'] - if len(keys) != 1: - print "Multiple event keys in", d - print 'File "%s", line %d' % (d['file'], d['line']) - key = keys[0] - if dd.has_key(key): - print "Duplicate event in", d - print 'File "%s", line %d' % (d['file'], d['line']) - return - dd[key] = d - else: - print "No event key in", d - print 'File "%s", line %d' % (d['file'], d['line']) - winevents = getevents(dd, "win") - unixevents = getevents(dd, "unix") - save = sys.stdout - f = open("keydefs.py", "w") - try: - sys.stdout = f - print "windows_keydefs = \\" - pprint.pprint(winevents) - print - print "unix_keydefs = \\" - pprint.pprint(unixevents) - finally: - sys.stdout = save - f.close() - -def getevents(dd, key): - res = {} - events = dd.keys() - events.sort() - for e in events: - d = dd[e] - if d.has_key(key) or d.has_key("all"): - list = [] - for x in d.get(key, []) + d.get("all", []): - list.append(x) - if key == "unix" and x[:5] == ">, and corresponding -methods, e.g. zoom_height_event(), and have one or more class (or instance) -variables that define mappings between virtual events and key sequences, -e.g. . When the extension is loaded, these key sequences will -be bound to the corresponding virtual events, and the virtual events -will be bound to the corresponding methods. (This indirection is done -so that the key bindings can easily be changed, and so that other -sources of virtual events can exist, such as menu entries.) - -The following class or instance variables are used to define key -bindings for virtual events: - - keydefs for all platforms - mac_keydefs for Macintosh - windows_keydefs for Windows - unix_keydefs for Unix (and other platforms) - -Each of these variables, if it exists, must be a dictionary whose -keys are virtual events, and whose values are lists of key sequences. - -An extension can define menu entries in a similar fashion. This is done -with a class or instance variable named menudefs; it should be a list of -pair, where each pair is a menu name (lowercase) and a list of menu -entries. Each menu entry is either None (to insert a separator entry) or -a pair of strings (menu_label, virtual_event). Here, menu_label is the -label of the menu entry, and virtual_event is the virtual event to be -generated when the entry is selected. An underscore in the menu label -is removed; the character following the underscore is displayed -underlined, to indicate the shortcut character (for Windows). - -At the moment, extensions cannot define whole new menus; they must -define entries in existing menus. Some menus are not present on some -windows; such entry definitions are then ignored, but the key bindings -are still applied. (This should probably be refined in the future.) - -Here is a complete example example: - -class ZoomHeight: - - menudefs = [ - ('edit', [ - None, # Separator - ('_Zoom Height', '<>'), - ]) - ] - - windows_keydefs = { - '<>': [''], - } - unix_keydefs = { - '<>': [''], - } - - def __init__(self, editwin): - self.editwin = editwin - - def zoom_height_event(self, event): - "...Do what you want here..." - -The final piece of the puzzle is the file "config.txt", which is used -to to configure the loading of extensions. For each extension, -you must include a section in config.txt (or in any of the other -configuration files that are consulted at startup: config-unix.txt, -config-win.txt, or ~/.idle). A section is headed by the module name -in square brackets, e.g. - - [ZoomHeight] - -The section may be empty, or it may define configuration options for -the extension. (See ParenMatch.py for an example.) A special option -is 'enable': including - - enable = 0 - -in a section disables that extension. More than one configuration -file may specify options for the same extension, so a user may disable -an extension that is loaded by default, or enable an extension that is -disabled by default. - -Extensions can define key bindings and menu entries that reference -events they don't implement (including standard events); however this is -not recommended (and may be forbidden in the future). - -Extensions are not required to define menu entries for all events they -implement. - -Note: in order to change key bindings, you must currently edit the file -keydefs. It contains two dictionaries named and formatted like the -keydefs dictionaries described above, one for the Unix bindings and one -for the Windows bindings. In the future, a better mechanism will be -provided. diff --git a/Lib/idlelib/help.txt b/Lib/idlelib/help.txt deleted file mode 100644 index 67e6e718b1e3..000000000000 --- a/Lib/idlelib/help.txt +++ /dev/null @@ -1,165 +0,0 @@ -[See end for tips.] - -Click on the dotted line at the top of a menu to "tear it off": a -separate window containing the menu is created. - -File menu: - - New window -- create a new editing window - Open... -- open an existing file - Open module... -- open an existing module (searches sys.path) - Class browser -- show classes and methods in current file - Path browser -- show sys.path directories, modules, classes - and methods - --- - Save -- save current window to the associated file (unsaved - windows have a * before and after the window title) - - Save As... -- save current window to new file, which becomes - the associated file - Save Copy As... -- save current window to different file - without changing the associated file - --- - Close -- close current window (asks to save if unsaved) - Exit -- close all windows and quit IDLE (asks to save if unsaved) - -Edit menu: - - Undo -- Undo last change to current window (max 1000 changes) - Redo -- Redo last undone change to current window - --- - Cut -- Copy selection into system-wide clipboard; then delete selection - Copy -- Copy selection into system-wide clipboard - Paste -- Insert system-wide clipboard into window - Select All -- Select the entire contents of the edit buffer - --- - Find... -- Open a search dialog box with many options - Find again -- Repeat last search - Find selection -- Search for the string in the selection - Find in Files... -- Open a search dialog box for searching files - Replace... -- Open a search-and-replace dialog box - Go to line -- Ask for a line number and show that line - --- - Indent region -- Shift selected lines right 4 spaces - Dedent region -- Shift selected lines left 4 spaces - Comment out region -- Insert ## in front of selected lines - Uncomment region -- Remove leading # or ## from selected lines - Tabify region -- Turns *leading* stretches of spaces into tabs - Untabify region -- Turn *all* tabs into the right number of spaces - Expand word -- Expand the word you have typed to match another - word in the same buffer; repeat to get a different expansion - Format Paragraph -- Reformat the current blank-line-separated paragraph - --- - Import module -- Import or reload the current module - Run script -- Execute the current file in the __main__ namespace - -Windows menu: - - Zoom Height -- toggles the window between normal size (24x80) - and maximum height. - --- - The rest of this menu lists the names of all open windows; - select one to bring it to the foreground (deiconifying it if - necessary). - -Debug menu (in the Python Shell window only): - - Go to file/line -- look around the insert point for a filename - and linenumber, open the file, and show the line - Open stack viewer -- show the stack traceback of the last exception - Debugger toggle -- Run commands in the shell under the debugger - JIT Stack viewer toggle -- Open stack viewer on traceback - -Basic editing and navigation: - - Backspace deletes to the left; DEL deletes to the right - Arrow keys and Page Up/Down to move around - Home/End go to begin/end of line - Control-Home/End go to begin/end of file - Some Emacs bindings may also work, e.g. ^B/^P/^A/^E/^D/^L - -Automatic indentation: - - After a block-opening statement, the next line is indented by - 4 spaces (in the Python Shell window by one tab). After - certain keywords (break, return etc.) the next line is - dedented. In leading indentation, Backspace deletes up to 4 - spaces if they are there. Tab inserts 1-4 spaces (in the - Python Shell window one tab). See also the indent/dedent - region commands in the edit menu. - -Python Shell window: - - ^C interrupts executing command - ^D sends end-of-file; closes window if typed at >>> prompt - - Command history: - - Alt-p retrieves previous command matching what you have typed - Alt-n retrieves next - Return while on any previous command retrieves that command - Alt-/ (Expand word) is also useful here - -Syntax colors: - - The coloring is applied in a background "thread", so you may - occasionally see uncolorized text. To change the color - scheme, edit the [Colors] section in config.txt (or add a - [Colors] section to ~/.idle). - - Python syntax colors: - - Keywords orange - Strings green - Comments red - Definitions blue - - Shell colors: - - Console output brown - stdout blue - stderr dark green - stdin black - -Other preferences: - - To change the font open config-[win/unix/mac].txt and - change - - font-name: courier new - font-size: 10 - - to, for example: - - font-name: courier new bold - font-size: 14 - - Note: a GUI based configuration screen will be provided - in the future. - - To change keyboard bindings, edit Bindings.py - -Command line usage: - - idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ... - - -c command run this command - -d enable debugger - -e edit mode; arguments are files to be edited - -s run $IDLESTARTUP or $PYTHONSTARTUP first - -t title set title of shell window - - If there are arguments: - - If -e is used, arguments are files opened for editing and - sys.argv reflects the arguments passed to IDLE itself. - - Otherwise, if -c is used, all arguments are placed in - sys.argv[1:...], with sys.argv[0] set to '-c'. - - Otherwise, if neither -e nor -c is used, the first - argument is a script which is executed with the remaining - arguments in sys.argv[1:...] and sys.argv[0] set to the - script name. If the script name is '-', no script is - executed but an interactive Python session is started; the - arguments are still available in sys.argv. diff --git a/Lib/idlelib/idle b/Lib/idlelib/idle deleted file mode 100755 index 8638a165b4b6..000000000000 --- a/Lib/idlelib/idle +++ /dev/null @@ -1,4 +0,0 @@ -#! /usr/bin/env python - -import PyShell -PyShell.main() diff --git a/Lib/idlelib/idle.bat b/Lib/idlelib/idle.bat deleted file mode 100755 index c1b5fd28acb3..000000000000 --- a/Lib/idlelib/idle.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -rem Working IDLE bat for Windows - uses start instead of absolute pathname -start idle.pyw %1 %2 %3 %4 %5 %6 %7 %8 %9 diff --git a/Lib/idlelib/idle.py b/Lib/idlelib/idle.py deleted file mode 100644 index 8638a165b4b6..000000000000 --- a/Lib/idlelib/idle.py +++ /dev/null @@ -1,4 +0,0 @@ -#! /usr/bin/env python - -import PyShell -PyShell.main() diff --git a/Lib/idlelib/idle.pyw b/Lib/idlelib/idle.pyw deleted file mode 100644 index 71fdce56bb1e..000000000000 --- a/Lib/idlelib/idle.pyw +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env python - -import os -import sys -import IdleConf - -idle_dir = os.path.split(sys.argv[0])[0] -IdleConf.load(idle_dir) - -# defer importing Pyshell until IdleConf is loaded -import PyShell -PyShell.main() diff --git a/Lib/idlelib/idlever.py b/Lib/idlelib/idlever.py deleted file mode 100644 index b5d70a7033aa..000000000000 --- a/Lib/idlelib/idlever.py +++ /dev/null @@ -1 +0,0 @@ -IDLE_VERSION = "0.8.2" diff --git a/Lib/idlelib/keydefs.py b/Lib/idlelib/keydefs.py deleted file mode 100644 index 455253aee0e9..000000000000 --- a/Lib/idlelib/keydefs.py +++ /dev/null @@ -1,55 +0,0 @@ -windows_keydefs = \ -{'<>': ['', ''], - '<>': ['', ''], - '<>': ['', ''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': ['']} - -unix_keydefs = \ -{'<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': ['', ''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': ['', ''], - '<>': [''], - '<>': [''], - '<>': [''], - '<>': ['', ''], - '<>': [''], - '<>': ['']} diff --git a/Lib/idlelib/loader.py b/Lib/idlelib/loader.py deleted file mode 100644 index 6a438c3c2a5e..000000000000 --- a/Lib/idlelib/loader.py +++ /dev/null @@ -1,64 +0,0 @@ -# Everything is done inside the loader function so that no other names -# are placed in the global namespace. Before user code is executed, -# even this name is unbound. -def loader(): - import sys, os, protocol, threading, time - import Remote - -## Use to debug the loading process itself: -## sys.stdout = open('c:\\windows\\desktop\\stdout.txt','a') -## sys.stderr = open('c:\\windows\\desktop\\stderr.txt','a') - - # Ensure that there is absolutely no pollution of the global - # namespace by deleting the global name of this function. - global loader - del loader - - # Connect to IDLE - try: - client = protocol.Client() - except protocol.connectionLost, cL: - print 'loader: Unable to connect to IDLE', cL - return - - # Connect to an ExecBinding object that needs our help. If - # the user is starting multiple programs right now, we might get a - # different one than the one that started us. Proving that's okay is - # left as an exercise to the reader. (HINT: Twelve, by the pigeonhole - # principle) - ExecBinding = client.getobject('ExecBinding') - if not ExecBinding: - print "loader: IDLE does not need me." - return - - # All of our input and output goes through ExecBinding. - sys.stdin = Remote.pseudoIn( ExecBinding.readline ) - sys.stdout = Remote.pseudoOut( ExecBinding.write.void, tag="stdout" ) - sys.stderr = Remote.pseudoOut( ExecBinding.write.void, tag="stderr" ) - - # Create a Remote object and start it running. - remote = Remote.Remote(globals(), ExecBinding) - rthread = threading.Thread(target=remote.mainloop) - rthread.setDaemon(1) - rthread.start() - - # Block until either the client or the user program stops - user = rthread.isAlive - while user and client.isAlive(): - time.sleep(0.025) - - if not user(): - user = hasattr(sys, "ready_to_exit") and sys.ready_to_exit - for t in threading.enumerate(): - if not t.isDaemon() and t.isAlive() and t!=threading.currentThread(): - user = t.isAlive - break - - # We need to make sure we actually exit, so that the user doesn't get - # stuck with an invisible process. We want to finalize C modules, so - # we don't use os._exit(), but we don't call sys.exitfunc, which might - # block forever. - del sys.exitfunc - sys.exit() - -loader() diff --git a/Lib/idlelib/protocol.py b/Lib/idlelib/protocol.py deleted file mode 100644 index f3f638253c5c..000000000000 --- a/Lib/idlelib/protocol.py +++ /dev/null @@ -1,369 +0,0 @@ -"""protocol (David Scherer ) - - This module implements a simple RPC or "distributed object" protocol. - I am probably the 100,000th person to write this in Python, but, hey, - it was fun. - - Contents: - - connectionLost is an exception that will be thrown by functions in - the protocol module or calls to remote methods that fail because - the remote program has closed the socket or because no connection - could be established in the first place. - - Server( port=None, connection_hook=None ) creates a server on a - well-known port, to which clients can connect. When a client - connects, a Connection is created for it. If connection_hook - is defined, then connection_hook( socket.getpeername() ) is called - before a Connection is created, and if it returns false then the - connection is refused. connection_hook must be prepared to be - called from any thread. - - Client( ip='127.0.0.1', port=None ) returns a Connection to a Server - object at a well-known address and port. - - Connection( socket ) creates an RPC connection on an arbitrary socket, - which must already be connected to another program. You do not - need to use this directly if you are using Client() or Server(). - - publish( name, connect_function ) provides an object with the - specified name to some or all Connections. When another program - calls Connection.getobject() with the specified name, the - specified connect_function is called with the arguments - - connect_function( conn, addr ) - - where conn is the Connection object to the requesting client and - addr is the address returned by socket.getpeername(). If that - function returns an object, that object becomes accessible to - the caller. If it returns None, the caller's request fails. - - Connection objects: - - .close() refuses additional RPC messages from the peer, and notifies - the peer that the connection has been closed. All pending remote - method calls in either program will fail with a connectionLost - exception. Further remote method calls on this connection will - also result in errors. - - .getobject(name) returns a proxy for the remote object with the - specified name, if it exists and the peer permits us access. - Otherwise, it returns None. It may throw a connectionLost - exception. The returned proxy supports basic attribute access - and method calls, and its methods have an extra attribute, - .void, which is a function that has the same effect but always - returns None. This last capability is provided as a performance - hack: object.method.void(params) can return without waiting for - the remote process to respond, but object.method(params) needs - to wait for a return value or exception. - - .rpc_loop(block=0) processes *incoming* messages for this connection. - If block=1, it continues processing until an exception or return - value is received, which is normally forever. Otherwise it - returns when all currently pending messages have been delivered. - It may throw a connectionLost exception. - - .set_close_hook(f) specifies a function to be called when the remote - object closes the connection during a call to rpc_loop(). This - is a good way for servers to be notified when clients disconnect. - - .set_shutdown_hook(f) specifies a function called *immediately* when - the receive loop detects that the connection has been lost. The - provided function must be prepared to run in any thread. - - Server objects: - - .rpc_loop() processes incoming messages on all connections, and - returns when all pending messages have been processed. It will - *not* throw connectionLost exceptions; the - Connection.set_close_hook() mechanism is much better for servers. -""" - -import sys, os, string, types -import socket -from threading import Thread -from Queue import Queue, Empty -from cPickle import Pickler, Unpickler, PicklingError - -class connectionLost: - def __init__(self, what=""): self.what = what - def __repr__(self): return self.what - def __str__(self): return self.what - -def getmethods(cls): - "Returns a list of the names of the methods of a class." - methods = [] - for b in cls.__bases__: - methods = methods + getmethods(b) - d = cls.__dict__ - for k in d.keys(): - if type(d[k])==types.FunctionType: - methods.append(k) - return methods - -class methodproxy: - "Proxy for a method of a remote object." - def __init__(self, classp, name): - self.classp=classp - self.name=name - self.client = classp.client - def __call__(self, *args, **keywords): - return self.client.call( 'm', self.classp.name, self.name, args, keywords ) - - def void(self, *args, **keywords): - self.client.call_void( 'm', self.classp.name,self.name,args,keywords) - -class classproxy: - "Proxy for a remote object." - def __init__(self, client, name, methods): - self.__dict__['client'] = client - self.__dict__['name'] = name - - for m in methods: - prox = methodproxy( self, m ) - self.__dict__[m] = prox - - def __getattr__(self, attr): - return self.client.call( 'g', self.name, attr ) - - def __setattr__(self, attr, value): - self.client.call_void( 's', self.name, attr, value ) - -local_connect = {} -def publish(name, connect_function): - local_connect[name]=connect_function - -class socketFile: - "File emulator based on a socket. Provides only blocking semantics for now." - - def __init__(self, socket): - self.socket = socket - self.buffer = '' - - def _recv(self,bytes): - try: - r=self.socket.recv(bytes) - except: - raise connectionLost() - if not r: - raise connectionLost() - return r - - def write(self, string): - try: - self.socket.send( string ) - except: - raise connectionLost() - - def read(self,bytes): - x = bytes-len(self.buffer) - while x>0: - self.buffer=self.buffer+self._recv(x) - x = bytes-len(self.buffer) - s = self.buffer[:bytes] - self.buffer=self.buffer[bytes:] - return s - - def readline(self): - while 1: - f = string.find(self.buffer,'\n') - if f>=0: - s = self.buffer[:f+1] - self.buffer=self.buffer[f+1:] - return s - self.buffer = self.buffer + self._recv(1024) - - -class Connection (Thread): - debug = 0 - def __init__(self, socket): - self.local_objects = {} - self.socket = socket - self.name = socket.getpeername() - self.socketfile = socketFile(socket) - self.queue = Queue(-1) - self.refuse_messages = 0 - self.cmds = { 'm': self.r_meth, - 'g': self.r_get, - 's': self.r_set, - 'o': self.r_geto, - 'e': self.r_exc, - #'r' handled by rpc_loop - } - - Thread.__init__(self) - self.setDaemon(1) - self.start() - - def getobject(self, name): - methods = self.call( 'o', name ) - if methods is None: return None - return classproxy(self, name, methods) - - # close_hook is called from rpc_loop(), like a normal remote method - # invocation - def set_close_hook(self,hook): self.close_hook = hook - - # shutdown_hook is called directly from the run() thread, and needs - # to be "thread safe" - def set_shutdown_hook(self,hook): self.shutdown_hook = hook - - close_hook = None - shutdown_hook = None - - def close(self): - self._shutdown() - self.refuse_messages = 1 - - def call(self, c, *args): - self.send( (c, args, 1 ) ) - return self.rpc_loop( block = 1 ) - - def call_void(self, c, *args): - try: - self.send( (c, args, 0 ) ) - except: - pass - - # the following methods handle individual RPC calls: - - def r_geto(self, obj): - c = local_connect.get(obj) - if not c: return None - o = c(self, self.name) - if not o: return None - self.local_objects[obj] = o - return getmethods(o.__class__) - - def r_meth(self, obj, name, args, keywords): - return apply( getattr(self.local_objects[obj],name), args, keywords) - - def r_get(self, obj, name): - return getattr(self.local_objects[obj],name) - - def r_set(self, obj, name, value): - setattr(self.local_objects[obj],name,value) - - def r_exc(self, e, v): - raise e, v - - def rpc_exec(self, cmd, arg, ret): - if self.refuse_messages: return - if self.debug: print cmd,arg,ret - if ret: - try: - r=apply(self.cmds.get(cmd), arg) - self.send( ('r', r, 0) ) - except: - try: - self.send( ('e', sys.exc_info()[:2], 0) ) - except PicklingError: - self.send( ('e', (TypeError, 'Unpicklable exception.'), 0 ) ) - else: - # we cannot report exceptions to the caller, so - # we report them in this process. - r=apply(self.cmds.get(cmd), arg) - - # the following methods implement the RPC and message loops: - - def rpc_loop(self, block=0): - if self.refuse_messages: raise connectionLost('(already closed)') - try: - while 1: - try: - cmd, arg, ret = self.queue.get( block ) - except Empty: - return None - if cmd=='r': return arg - self.rpc_exec(cmd,arg,ret) - except connectionLost: - if self.close_hook: - self.close_hook() - self.close_hook = None - raise - - def run(self): - try: - while 1: - data = self.recv() - self.queue.put( data ) - except: - self.queue.put( ('e', sys.exc_info()[:2], 0) ) - - # The following send raw pickled data to the peer - - def send(self, data): - try: - Pickler(self.socketfile,1).dump( data ) - except connectionLost: - self._shutdown() - if self.shutdown_hook: self.shutdown_hook() - raise - - def recv(self): - try: - return Unpickler(self.socketfile).load() - except connectionLost: - self._shutdown() - if self.shutdown_hook: self.shutdown_hook() - raise - except: - raise - - def _shutdown(self): - try: - self.socket.shutdown(1) - self.socket.close() - except: - pass - - -class Server (Thread): - default_port = 0x1D1E # "IDlE" - - def __init__(self, port=None, connection_hook=None): - self.connections = [] - self.port = port or self.default_port - self.connection_hook = connection_hook - - try: - self.wellknown = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(('', self.port)) - s.listen(3) - except: - raise connectionLost - - Thread.__init__(self) - self.setDaemon(1) - self.start() - - def run(self): - s = self.wellknown - while 1: - conn, addr = s.accept() - if self.connection_hook and not self.connection_hook(addr): - try: - conn.shutdown(1) - except: - pass - continue - self.connections.append( Connection(conn) ) - - def rpc_loop(self): - cns = self.connections[:] - for c in cns: - try: - c.rpc_loop(block = 0) - except connectionLost: - if c in self.connections: - self.connections.remove(c) - -def Client(ip='127.0.0.1', port=None): - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((ip,port or Server.default_port)) - except socket.error, what: - raise connectionLost(str(what)) - except: - raise connectionLost() - return Connection(s) diff --git a/Lib/idlelib/setup.py b/Lib/idlelib/setup.py deleted file mode 100644 index e6835120959e..000000000000 --- a/Lib/idlelib/setup.py +++ /dev/null @@ -1,86 +0,0 @@ -import os,glob -from distutils.core import setup -from distutils.command.build_py import build_py -from distutils.command.install_lib import install_lib -import idlever - -# name of idle package -idlelib = "idlelib" - -# the normal build_py would not incorporate the .txt files -txt_files = ['config-unix.txt','config-win.txt','config.txt', 'help.txt'] -Icons = glob.glob1("Icons","*.gif") -class idle_build_py(build_py): - def get_plain_outfile(self, build_dir, package, file): - # like get_module_outfile, but does not append .py - outfile_path = [build_dir] + list(package) + [file] - return apply(os.path.join, outfile_path) - - def run(self): - # Copies all .py files, then also copies the txt and gif files - build_py.run(self) - assert self.packages == [idlelib] - for name in txt_files: - outfile = self.get_plain_outfile(self.build_lib, [idlelib], name) - dir = os.path.dirname(outfile) - self.mkpath(dir) - self.copy_file(name, outfile, preserve_mode = 0) - for name in Icons: - outfile = self.get_plain_outfile(self.build_lib, - [idlelib,"Icons"], name) - dir = os.path.dirname(outfile) - self.mkpath(dir) - self.copy_file(os.path.join("Icons",name), - outfile, preserve_mode = 0) - - def get_source_files(self): - # returns the .py files, the .txt files, and the icons - icons = [os.path.join("Icons",name) for name in Icons] - return build_py.get_source_files(self)+txt_files+icons - - def get_outputs(self, include_bytecode=1): - # returns the built files - outputs = build_py.get_outputs(self, include_bytecode) - if not include_bytecode: - return outputs - for name in txt_files: - filename = self.get_plain_outfile(self.build_lib, - [idlelib], name) - outputs.append(filename) - for name in Icons: - filename = self.get_plain_outfile(self.build_lib, - [idlelib,"Icons"], name) - outputs.append(filename) - return outputs - -# Arghhh. install_lib thinks that all files returned from build_py's -# get_outputs are bytecode files - -class idle_install_lib(install_lib): - def _bytecode_filenames(self, files): - files = [n for n in files if n.endswith('.py')] - return install_lib._bytecode_filenames(self,files) - -setup(name="IDLE", - version = idlever.IDLE_VERSION, - description = "IDLE Fork, the Forked Python IDE", - author = "Guido van Rossum", - author_email = "guido@python.org", - #url = - long_description = -"""IDLE is a Tkinter based IDE for Python. It is written in 100% pure -Python and works both on Windows and Unix. It features a multi-window -text editor with multiple undo, Python colorizing, and many other things, -as well as a Python shell window and a debugger. - -IDLE Fork is a separate line of development which was initiated by D. Scherer -at CMU as part of Visual Python. It features execution in a separate -process, with a fresh environment for each run. For further details, -refer to idlefork.sourceforge.net.""", - - cmdclass = {'build_py':idle_build_py, - 'install_lib':idle_install_lib}, - package_dir = {idlelib:'.'}, - packages = [idlelib], - scripts = ['idle'] - ) diff --git a/Lib/idlelib/spawn.py b/Lib/idlelib/spawn.py deleted file mode 100644 index be8fdf777275..000000000000 --- a/Lib/idlelib/spawn.py +++ /dev/null @@ -1,58 +0,0 @@ -# spawn - This is ugly, OS-specific code to spawn a separate process. It -# also defines a function for getting the version of a path most -# likely to work with cranky API functions. - -import os - -def hardpath(path): - path = os.path.normcase(os.path.abspath(path)) - try: - import win32api - path = win32api.GetShortPathName( path ) - except: - pass - return path - -if hasattr(os, 'fork'): - - # UNIX-ish operating system: we fork() and exec(), and we have to track - # the pids of our children and call waitpid() on them to avoid leaving - # zombies in the process table. kill_zombies() does the dirty work, and - # should be called periodically. - - zombies = [] - - def spawn(bin, *args): - pid = os.fork() - if pid: - zombies.append(pid) - else: - os.execv( bin, (bin, ) + args ) - - def kill_zombies(): - for z in zombies[:]: - stat = os.waitpid(z, os.WNOHANG) - if stat[0]==z: - zombies.remove(z) -elif hasattr(os, 'spawnv'): - - # Windows-ish OS: we use spawnv(), and stick quotes around arguments - # in case they contains spaces, since Windows will jam all the - # arguments to spawn() or exec() together into one string. The - # kill_zombies function is a noop. - - def spawn(bin, *args): - nargs = ['"'+bin+'"'] - for arg in args: - nargs.append( '"'+arg+'"' ) - os.spawnv( os.P_NOWAIT, bin, nargs ) - - def kill_zombies(): pass - -else: - # If you get here, you may be able to write an alternative implementation - # of these functions for your OS. - - def kill_zombies(): pass - - raise OSError, 'This OS does not support fork() or spawnv().' diff --git a/Lib/idlelib/tabpage.py b/Lib/idlelib/tabpage.py deleted file mode 100644 index fcd074cd0047..000000000000 --- a/Lib/idlelib/tabpage.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -a couple of classes for implementing partial tabbed-page like behaviour -""" - -from Tkinter import * - -class PageTab(Frame): - """ - a 'page tab' like framed button - """ - def __init__(self,parent): - Frame.__init__(self, parent,borderwidth=2,relief=RIDGE) - self.button=Radiobutton(self,padx=5,pady=5,takefocus=FALSE, - underline=0,indicatoron=FALSE,highlightthickness=0, - borderwidth=0,selectcolor=self.cget('bg')) - self.button.pack() - -class TabPageSet(Frame): - """ - a set of 'pages' with TabButtons for controlling their display - """ - def __init__(self,parent,pageNames,**kw): - """ - pageNames - a list of strings, each string will be the dictionary key - to a page's data, and the name displayed on the page's tab. Should be - specified in desired page order. The first page will be the default - and first active page. - """ - Frame.__init__(self, parent, kw) - self.grid_location(0,0) - self.columnconfigure(0,weight=1) - self.rowconfigure(1,weight=1) - self.tabBar=Frame(self) - self.tabBar.grid(row=0,column=0,sticky=EW) - self.activePage=StringVar(self) - self.defaultPage='' - self.pages={} - for name in pageNames: - self.AddPage(name) - - def ChangePage(self,pageName=None): - if pageName: - if pageName in self.pages.keys(): - self.activePage.set(pageName) - else: - raise 'Invalid TabPage Name' - ## pop up the active 'tab' only - for page in self.pages.keys(): - self.pages[page]['tab'].config(relief=RIDGE) - self.pages[self.GetActivePage()]['tab'].config(relief=RAISED) - ## switch page - self.pages[self.GetActivePage()]['page'].lift() - - def GetActivePage(self): - return self.activePage.get() - - def AddPage(self,pageName): - if pageName in self.pages.keys(): - raise 'TabPage Name Already Exists' - self.pages[pageName]={'tab':PageTab(self.tabBar), - 'page':Frame(self,borderwidth=2,relief=RAISED)} - self.pages[pageName]['tab'].button.config(text=pageName, - command=self.ChangePage,variable=self.activePage, - value=pageName) - self.pages[pageName]['tab'].pack(side=LEFT) - self.pages[pageName]['page'].grid(row=1,column=0,sticky=NSEW) - if len(self.pages)==1: # adding first page - self.defaultPage=pageName - self.activePage.set(self.defaultPage) - self.ChangePage() - - def RemovePage(self,pageName): - if not pageName in self.pages.keys(): - raise 'Invalid TabPage Name' - self.pages[pageName]['tab'].pack_forget() - self.pages[pageName]['page'].grid_forget() - self.pages[pageName]['tab'].destroy() - self.pages[pageName]['page'].destroy() - del(self.pages[pageName]) - # handle removing last remaining, or default, or active page - if not self.pages: # removed last remaining page - self.defaultPage='' - return - if pageName==self.defaultPage: # set a new default page - self.defaultPage=\ - self.tabBar.winfo_children()[0].button.cget('text') - if pageName==self.GetActivePage(): # set a new active page - self.activePage.set(self.defaultPage) - self.ChangePage() - -if __name__ == '__main__': - #test dialog - root=Tk() - tabPage=TabPageSet(root,pageNames=['Foobar','Baz']) - tabPage.pack(expand=TRUE,fill=BOTH) - Label(tabPage.pages['Foobar']['page'],text='Foo',pady=20).pack() - Label(tabPage.pages['Foobar']['page'],text='Bar',pady=20).pack() - Label(tabPage.pages['Baz']['page'],text='Baz').pack() - entryPgName=Entry(root) - buttonAdd=Button(root,text='Add Page', - command=lambda:tabPage.AddPage(entryPgName.get())) - buttonRemove=Button(root,text='Remove Page', - command=lambda:tabPage.RemovePage(entryPgName.get())) - labelPgName=Label(root,text='name of page to add/remove:') - buttonAdd.pack(padx=5,pady=5) - buttonRemove.pack(padx=5,pady=5) - labelPgName.pack(padx=5) - entryPgName.pack(padx=5) - root.mainloop() - diff --git a/Lib/idlelib/testcode.py b/Lib/idlelib/testcode.py deleted file mode 100644 index 05eaa562cd1b..000000000000 --- a/Lib/idlelib/testcode.py +++ /dev/null @@ -1,31 +0,0 @@ -import string - -def f(): - a = 0 - b = 1 - c = 2 - d = 3 - e = 4 - g() - -def g(): - h() - -def h(): - i() - -def i(): - j() - -def j(): - k() - -def k(): - l() - -l = lambda: test() - -def test(): - string.capwords(1) - -f() diff --git a/Lib/idlelib/textView.py b/Lib/idlelib/textView.py deleted file mode 100644 index 9b3fb979d43c..000000000000 --- a/Lib/idlelib/textView.py +++ /dev/null @@ -1,77 +0,0 @@ -##---------------------------------------------------------------------------## -## -## idle - simple text view dialog -## elguavas -## -##---------------------------------------------------------------------------## -""" -simple text browser for idle -""" -from Tkinter import * -import tkMessageBox - -class TextViewer(Toplevel): - """ - simple text viewer dialog for idle - """ - def __init__(self,parent,title,fileName): - """ - fileName - string,should be an absoulute filename - """ - Toplevel.__init__(self, parent) - self.configure(borderwidth=5) - self.geometry("+%d+%d" % (parent.winfo_rootx()+10, - parent.winfo_rooty()+10)) - #elguavas - config placeholders til config stuff completed - self.bg=None - self.fg=None - - self.CreateWidgets() - self.title(title) - self.transient(parent) - self.grab_set() - self.protocol("WM_DELETE_WINDOW", self.Ok) - self.parent = parent - self.textView.focus_set() - #key bindings for this dialog - self.bind('',self.Ok) #dismiss dialog - self.bind('',self.Ok) #dismiss dialog - self.LoadTextFile(fileName) - self.textView.config(state=DISABLED) - self.wait_window() - - def LoadTextFile(self, fileName): - textFile = None - try: - textFile = open(fileName, 'r') - except IOError: - tkMessageBox.showerror(title='File Load Error', - message='Unable to load file '+`fileName`+' .') - else: - self.textView.insert(0.0,textFile.read()) - - def CreateWidgets(self): - frameText = Frame(self) - frameButtons = Frame(self) - self.buttonOk = Button(frameButtons,text='Ok', - command=self.Ok,takefocus=FALSE,default=ACTIVE) - self.scrollbarView = Scrollbar(frameText,orient=VERTICAL, - takefocus=FALSE,highlightthickness=0) - self.textView = Text(frameText,wrap=WORD,highlightthickness=0) - self.scrollbarView.config(command=self.textView.yview) - self.textView.config(yscrollcommand=self.scrollbarView.set) - self.buttonOk.pack(padx=5,pady=5) - 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) - - def Ok(self, event=None): - self.destroy() - -if __name__ == '__main__': - #test the dialog - root=Tk() - Button(root,text='View', - command=lambda:TextViewer(root,'Text','./textView.py')).pack() - root.mainloop() diff --git a/Lib/test/test_cpickle.py b/Lib/test/test_cpickle.py index dda606f3a446..874735bfceb9 100644 --- a/Lib/test/test_cpickle.py +++ b/Lib/test/test_cpickle.py @@ -80,6 +80,13 @@ class cPickleFastPicklerTests(AbstractPickleTests): AbstractPickleTests.test_recursive_multi, self) + def test_nonrecursive_deep(self): + a = [] + for i in range(100): + a = [a] + b = self.loads(self.dumps(a)) + self.assertEqual(a, b) + def test_main(): loader = unittest.TestLoader() suite = unittest.TestSuite() diff --git a/Mac/Build/PythonCore.mcp b/Mac/Build/PythonCore.mcp index 03e46e16f918e15eab149a16fb9178d676f442d9..140ce158e73214e95a60e529fd592a6f3eb119c6 100644 GIT binary patch delta 2399 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 literal 673637 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 p>0+21x#SgOVM~L83+JPpV(~Yi1y!|$3#z, http://www.cwi.nl/~jack diff --git a/Mac/Relnotes b/Mac/Relnotes index 3d99c13e37c3..ce67bdb84009 100644 --- a/Mac/Relnotes +++ b/Mac/Relnotes @@ -1,8 +1,8 @@ -Changes in 2.2c1 since 2.1.1 +Changes in 2.2 since 2.1.1 ---------------------------- These release notes refer to Mac-specific changes only. See NEWS (in the Misc folder) -for machine-independent changes. Changes that are new in 2.2c1 are flagged as such. +for machine-independent changes. - The main change is that all toolbox modules have moved to a package called Carbon. @@ -11,27 +11,27 @@ for machine-independent changes. Changes that are new in 2.2c1 are flagged as su some open questions and join the discussions on pythonmac-sig if you have anything to contribute. Aside from reducing clutter this change will also benefit the port to Mach-O/OSX Python later. -- All toolbox modules have been updated to Universal Headers 3.4. [2.2c1] +- All toolbox modules have been updated to Universal Headers 3.4. - Toolbox modules are weaklinked against InterfaceLib (for PPC builds) and raise - an exception when you call an unimplemented one on an old MacOS. [2.2c1] + an exception when you call an unimplemented one on an old MacOS. - On input MacPython now accepts either \n (unix style) or \r (mac style) newlines for text files. This behaviour can be turned off with a preference. This is an experimental feature; again: feedback is requested. -- The IDE looks better on OS X, but still not as good as on OS9. [2.2c1] +- The IDE looks better on OS X, but still not as good as on OS9. - Command-dot handling has been improved a lot: scripts are now much easier to interrupt, and they only scan for cmd-. while in the foreground. - "Copy" from the MacPython console window was always disabled. Fixed. - This release should run on MacOS 8.1 again. - A new, rather different GUSI I/O library is used. -- time.time() returns positive values again. [2.2c1] +- time.time() returns positive values again. - There is a new module macresource which makes it easier to open a resource file accompanying your script when the script is not (yet) converted to an applet. This module will later also do the right thing in Mach-O/OSX Python. - (Carbon only) experimental modules Carbon.CG (CoreGraphics) and CarbonEvt have - been added. [2.2c1] + been added. - A new, experimental module hfsplus is included, which gives access to some of the functionality of the HFS+ API. -- A new, experimental module gives access to Carbon Events. [2.2c1] +- A new, experimental module gives access to Carbon Events. - Threads had a stack that was too small for many serious Python applications (20K). They now get 64K. There is still no overflow check, though. - Garbage collection and the gc module have (finally) been enabled. @@ -52,11 +52,6 @@ for machine-independent changes. Changes that are new in 2.2c1 are flagged as su - Contrib:morefindertools is gone, the functionality has been integrated into the standard module findertools.py. -What is not in this distribution --------------------------------- - -- The toolbox modules have not all been updated to Universal Header 3.4 or CarbonLib 1.4 yet. - Known problems -------------- @@ -65,7 +60,8 @@ http://www.cwi.nl/~jack/macpython.html. - MacPython 2.2 (and MacPython 2.1) will not run correctly on a multiprocessor MacOS X machine, it will quickly deadlock during I/O operations. The GUSI I/O library is suspected, - hints/clues/workarounds are solicited. + hints/clues/workarounds are solicited. This problem also occurs intermittently on fast + OS X single-processor machines. - Tkinter does not work under Carbon. - The IDE and Tkinter do not work together. Run tkinter programs under PythonInterpreter. - Tkinter file events do not work, unless you have opened the file through Tcl (but then diff --git a/Mac/Unsupported/mactcp/dnrglue.c b/Mac/Unsupported/mactcp/dnrglue.c deleted file mode 100644 index 5474b73cedaf..000000000000 --- a/Mac/Unsupported/mactcp/dnrglue.c +++ /dev/null @@ -1,301 +0,0 @@ -/* DNR.c - DNR library for MPW - - (c) Copyright 1988 by Apple Computer. All rights reserved - - Modifications by Jim Matthews, Dartmouth College, 5/91 - Again modified for use with python by Jack Jansen, CWI, October 1994. - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "AddressXlation.h" - -TrapType GetTrapType(unsigned long theTrap); -Boolean TrapAvailable(unsigned long trap); -void GetSystemFolder(short *vRefNumP, long *dirIDP); -void GetCPanelFolder(short *vRefNumP, long *dirIDP); -short SearchFolderForDNRP(long targetType, long targetCreator, short vRefNum, long dirID); -short OpenOurRF(void); - -#define OPENRESOLVER 1L -#define CLOSERESOLVER 2L -#define STRTOADDR 3L -#define ADDRTOSTR 4L -#define ENUMCACHE 5L -#define ADDRTONAME 6L -#define HINFO 7L -#define MXINFO 8L - -Handle codeHndl = nil; - -OSErrProcPtr dnr = nil; - -TrapType GetTrapType(theTrap) -unsigned long theTrap; -{ - if (BitAnd(theTrap, 0x0800) > 0) - return(ToolTrap); - else - return(OSTrap); - } - -Boolean TrapAvailable(trap) -unsigned long trap; -{ -TrapType trapType = ToolTrap; -unsigned long numToolBoxTraps; - - if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap)) - numToolBoxTraps = 0x200; - else - numToolBoxTraps = 0x400; - - trapType = GetTrapType(trap); - if (trapType == ToolTrap) { - trap = BitAnd(trap, 0x07FF); - if (trap >= numToolBoxTraps) - trap = _Unimplemented; - } - return(NGetTrapAddress(trap, trapType) != NGetTrapAddress(_Unimplemented, ToolTrap)); - -} - -void GetSystemFolder(short *vRefNumP, long *dirIDP) -{ - SysEnvRec info; - long wdProcID; - - SysEnvirons(1, &info); - if (GetWDInfo(info.sysVRefNum, vRefNumP, dirIDP, &wdProcID) != noErr) { - *vRefNumP = 0; - *dirIDP = 0; - } - } - -void GetCPanelFolder(short *vRefNumP, long *dirIDP) -{ - Boolean hasFolderMgr = false; - long feature; - - if (Gestalt(gestaltFindFolderAttr, &feature) == noErr) hasFolderMgr = true; - if (!hasFolderMgr) { - GetSystemFolder(vRefNumP, dirIDP); - return; - } - else { - if (FindFolder(kOnSystemDisk, kControlPanelFolderType, kDontCreateFolder, vRefNumP, dirIDP) != noErr) { - *vRefNumP = 0; - *dirIDP = 0; - } - } - } - -/* SearchFolderForDNRP is called to search a folder for files that might - contain the 'dnrp' resource */ -short SearchFolderForDNRP(long targetType, long targetCreator, short vRefNum, long dirID) -{ - HParamBlockRec fi; - Str255 filename; - short refnum; - - fi.fileParam.ioCompletion = nil; - fi.fileParam.ioNamePtr = filename; - fi.fileParam.ioVRefNum = vRefNum; - fi.fileParam.ioDirID = dirID; - fi.fileParam.ioFDirIndex = 1; - - while (PBHGetFInfo(&fi, false) == noErr) { - /* scan system folder for driver resource files of specific type & creator */ - if (fi.fileParam.ioFlFndrInfo.fdType == targetType && - fi.fileParam.ioFlFndrInfo.fdCreator == targetCreator) { - /* found the MacTCP driver file? */ - refnum = HOpenResFile(vRefNum, dirID, filename, fsRdPerm); - if (GetIndResource('dnrp', 1) == NULL) - CloseResFile(refnum); - else - return refnum; - } - /* check next file in system folder */ - fi.fileParam.ioFDirIndex++; - fi.fileParam.ioDirID = dirID; /* PBHGetFInfo() clobbers ioDirID */ - } - return(-1); - } - -/* OpenOurRF is called to open the MacTCP driver resources */ - -short OpenOurRF() -{ - short refnum; - short vRefNum; - long dirID; - - /* first search Control Panels for MacTCP 1.1 */ - GetCPanelFolder(&vRefNum, &dirID); - refnum = SearchFolderForDNRP('cdev', 'ztcp', vRefNum, dirID); - if (refnum != -1) return(refnum); - - /* next search System Folder for MacTCP 1.0.x */ - GetSystemFolder(&vRefNum, &dirID); - refnum = SearchFolderForDNRP('cdev', 'mtcp', vRefNum, dirID); - if (refnum != -1) return(refnum); - - /* finally, search Control Panels for MacTCP 1.0.x */ - GetCPanelFolder(&vRefNum, &dirID); - refnum = SearchFolderForDNRP('cdev', 'mtcp', vRefNum, dirID); - if (refnum != -1) return(refnum); - - return -1; - } - - -OSErr OpenResolver(fileName) -char *fileName; -{ - short refnum; - OSErr rc; - - if (dnr != nil) - /* resolver already loaded in */ - return(noErr); - - /* open the MacTCP driver to get DNR resources. Search for it based on - creator & type rather than simply file name */ - refnum = OpenOurRF(); - - /* ignore failures since the resource may have been installed in the - System file if running on a Mac 512Ke */ - - /* load in the DNR resource package */ - codeHndl = GetIndResource('dnrp', 1); - if (codeHndl == nil) { - /* can't open DNR */ - return(ResError()); - } - - DetachResource(codeHndl); - if (refnum != -1) { - CloseWD(refnum); - CloseResFile(refnum); - } - - /* lock the DNR resource since it cannot be reloated while opened */ - HLock(codeHndl); - dnr = (OSErrProcPtr) *codeHndl; - - /* call open resolver */ - rc = (*dnr)(OPENRESOLVER, fileName); - if (rc != noErr) { - /* problem with open resolver, flush it */ - HUnlock(codeHndl); - DisposHandle(codeHndl); - dnr = nil; - } - return(rc); - } - - -OSErr CloseResolver() -{ - if (dnr == nil) - /* resolver not loaded error */ - return(notOpenErr); - - /* call close resolver */ - (void) (*dnr)(CLOSERESOLVER); - - /* release the DNR resource package */ - HUnlock(codeHndl); - DisposHandle(codeHndl); - dnr = nil; - return(noErr); - } - -OSErr StrToAddr(hostName, rtnStruct, resultproc, userDataPtr) -char *hostName; -struct hostInfo *rtnStruct; -ResultProcPtr resultproc; -char *userDataPtr; -{ - if (dnr == nil) - /* resolver not loaded error */ - return(notOpenErr); - - return((*dnr)(STRTOADDR, hostName, rtnStruct, resultproc, userDataPtr)); - } - -OSErr AddrToStr(addr, addrStr) -unsigned long addr; -char *addrStr; -{ - if (dnr == nil) - /* resolver not loaded error */ - return(notOpenErr); - - (*dnr)(ADDRTOSTR, addr, addrStr); - return(noErr); - } - -OSErr EnumCache(resultproc, userDataPtr) -EnumResultProcPtr resultproc; -char *userDataPtr; -{ - if (dnr == nil) - /* resolver not loaded error */ - return(notOpenErr); - - return((*dnr)(ENUMCACHE, resultproc, userDataPtr)); - } - - -OSErr AddrToName(addr, rtnStruct, resultproc, userDataPtr) -unsigned long addr; -struct hostInfo *rtnStruct; -ResultProcPtr resultproc; -char *userDataPtr; -{ - if (dnr == nil) - /* resolver not loaded error */ - return(notOpenErr); - - return((*dnr)(ADDRTONAME, addr, rtnStruct, resultproc, userDataPtr)); - } - - -extern OSErr HInfo(hostName, returnRecPtr, resultProc, userDataPtr) -char *hostName; -struct returnRec *returnRecPtr; -ResultProc2Ptr resultProc; -char *userDataPtr; -{ - if (dnr == nil) - /* resolver not loaded error */ - return(notOpenErr); - - return((*dnr)(HINFO, hostName, returnRecPtr, resultProc, userDataPtr)); - - } - -extern OSErr MXInfo(hostName, returnRecPtr, resultProc, userDataPtr) -char *hostName; -struct returnRec *returnRecPtr; -ResultProc2Ptr resultProc; -char *userDataPtr; -{ - if (dnr == nil) - /* resolver not loaded error */ - return(notOpenErr); - - return((*dnr)(MXINFO, hostName, returnRecPtr, resultProc, userDataPtr)); - - } \ No newline at end of file diff --git a/Mac/_checkversion.py b/Mac/_checkversion.py index 443b9b59b2f6..79cc5914cb8c 100644 --- a/Mac/_checkversion.py +++ b/Mac/_checkversion.py @@ -5,7 +5,7 @@ _checkversion.py file""" import pyversioncheck _PACKAGE="MacPython" -_VERSION="2.2b1" +_VERSION="2.2" _URL="http://www.cwi.nl/~jack/macpythonversion.txt" try: diff --git a/Misc/NEWS b/Misc/NEWS index eafe10ea1f1d..323aae74e4b2 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -4,12 +4,38 @@ Release date: 21-Dec-2001 Type/class unification and new-style classes +- pickle.py, cPickle: allow pickling instances of new-style classes + with a custom metaclass. + Core and builtins +- weakref proxy object: when comparing, unwrap both arguments if both + are proxies. + Extension modules +- binascii.b2a_base64(): fix a potential buffer overrun when encoding + very short strings. + +- cPickle: the obscure "fast" mode was suspected of causing stack + overflows on the Mac. Hopefully fixed this by setting the recursion + limit much smaller. If the limit is too low (it only affects + performance), you can change it by defining PY_CPICKLE_FAST_LIMIT + when compiling cPickle.c (or in pyconfig.h). + Library +- dumbdbm.py: fixed a dumb old bug (the file didn't get synched at + close or delete time). + +- rfc822.py: fixed a bug where the address '<>' was converted to None + instead of an empty string (also fixes the email.Utils module). + +- xmlrpclib.py: version 1.0.0; uses precision for doubles. + +- test suite: the pickle and cPickle tests were not executing any code + when run from the standard regresssion test. + Tools/Demos Build @@ -22,8 +48,23 @@ Tests Windows +- distutils package: fixed broken Windows installers (bdist_wininst). + +- tempfile.py: prevent mysterious warnings when TemporaryFileWrapper + instances are deleted at process exit time. + +- socket.py: prevent mysterious warnings when socket instances are + deleted at process exit time. + +- posixmodule.c: fix a Windows crash with stat() of a filename ending + in backslash. + Mac +- The Carbon toolbox modules have been upgraded to Universal Headers + 3.4, and experimental CoreGraphics and CarbonEvents modules have + been added. All only for framework-enabled MacOSX. + What's New in Python 2.2c1? Release date: 14-Dec-2001 diff --git a/Modules/cPickle.c b/Modules/cPickle.c index a4943ce8211e..adf7e4449f02 100644 --- a/Modules/cPickle.c +++ b/Modules/cPickle.c @@ -321,7 +321,9 @@ typedef struct Picklerobject { PyObject *fast_memo; } Picklerobject; -#define FAST_LIMIT 2000 +#ifndef PY_CPICKLE_FAST_LIMIT +#define PY_CPICKLE_FAST_LIMIT 50 +#endif staticforward PyTypeObject Picklertype; @@ -891,7 +893,7 @@ static int fast_save_enter(Picklerobject *self, PyObject *obj) { /* if fast_container < 0, we're doing an error exit. */ - if (++self->fast_container >= FAST_LIMIT) { + if (++self->fast_container >= PY_CPICKLE_FAST_LIMIT) { PyObject *key = NULL; if (self->fast_memo == NULL) { self->fast_memo = PyDict_New(); @@ -921,7 +923,7 @@ fast_save_enter(Picklerobject *self, PyObject *obj) int fast_save_leave(Picklerobject *self, PyObject *obj) { - if (self->fast_container-- >= FAST_LIMIT) { + if (self->fast_container-- >= PY_CPICKLE_FAST_LIMIT) { PyObject *key = PyLong_FromVoidPtr(obj); if (key == NULL) return 0; diff --git a/Python/mysnprintf.c b/Python/mysnprintf.c index e3b72de2bf00..4d3770d89435 100644 --- a/Python/mysnprintf.c +++ b/Python/mysnprintf.c @@ -65,7 +65,7 @@ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) len = vsnprintf(str, size, format, va); #else /* Emulate it. */ - buffer = PyMem_Malloc(size + 512); + buffer = PyMem_MALLOC(size + 512); if (buffer == NULL) { len = -666; goto Done; @@ -85,7 +85,7 @@ PyOS_vsnprintf(char *str, size_t size, const char *format, va_list va) memcpy(str, buffer, to_copy); str[to_copy] = '\0'; } - PyMem_Free(buffer); + PyMem_FREE(buffer); Done: #endif str[size-1] = '\0'; -- 2.47.3