>> I'm using Windows XP, using IDLE (which was mentioned already) > in the context of editing/displaying code, not executing it. Does the > problem occur before or after you edit a file with IDLE?
Actually, neither. I'm not editing the code. I open it in IDLE in 2.5 and attempt to run it through the Run menu "Run Module" menu item or by pressing F5. It immediately fails with a tabnanny error. If I run it from 2.4's IDLE, it works. >> and I >> downloaded the 2.5.1 exe/msi file from python.org to install it. > What you downloaded doesn't answer the question about how you > installed it. Do you still have your 2.4 installation? Yes, I use both 2.4 and 2.5 as I migrate from one to the other. I've attached a file that causes it consistently. On two systems with both 2.4 and 2.5 installed, it fails on line 206 when run from the IDLE included with 2.5. >> I have yet to find a simple one which exhibits the issue to post. It >> seems to happen to my complex files, not the simple ones. > So chop up a complex file ... > Have you inspected the failing files using a text editor that can > display tabs and spaces (e.g. PythonWin, TextPad)? I just used Notepad++ to inspect the file and it does indeed have tabs at line 206 rather than spaces. I take it that Python 2.5 is more sensitive to that than is 2.4? I program almost exclusively in IDLE as I'd read that this can happen in some text editors, but it seemed implied that it didn't if you used IDLE. At least, that's what I got from various Python books and the website: http://www.python.org/idle/doc/idlemain.html Anyway, thanks for the help. Mike
# scrubber.pyw # # Author: Mike Driscoll # # Updated: 05/16/2007 # # Deletes folders and files, ignores errors. The deletion functions # run in a separate thread. import wx import os import glob import sys import shutil import time import win32api from threading import Thread userid = win32api.GetUserName() class ProfileScrubber(wx.App): def __init__(self, redirect=True, filename=None): wx.App.__init__(self, redirect, filename) def OnInit(self): self.frame = wx.Frame(None, -1, title='Profile Scrubber Beta 0.3', size=(400,500)) panel = wx.Panel(self.frame, -1) # user list up-to-date as of 10/20/2006 user_list = self.getProfileList() tempF = r'C:\Documents and Settings\%s\Local Settings\Temp' % userid tempIntF = r'C:\Documents and Settings\%s\Local Settings\Temporary Internet Files' % userid # Create the controls descriptionLbl = wx.StaticText(panel, -1, ' Choose an option below:') font = wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD) descriptionLbl.SetFont(font) genericPathLbl = wx.StaticText(panel, -1, 'Enter Path:') self.path = wx.TextCtrl(panel, -1, '', size=(500,-1)) optionLbl = wx.StaticText(panel, -1, 'Other Folders to be Deleted:') optionLbl.SetFont(font) self.tempCkbx = wx.CheckBox(panel, -1, 'Temp', size=wx.DefaultSize) self.tempCkbx.SetValue(True) self.tempFilesCkbx = wx.CheckBox(panel, -1, 'Temporary Internet Files', size=wx.DefaultSize) self.tempFilesCkbx.SetValue(True) self.tempTxt = wx.TextCtrl(panel, -1, tempF, size=(500,-1)) self.tempTxt.Disable() self.tempFilesTxt = wx.TextCtrl(panel, -1, tempIntF, size=(500,-1)) self.tempFilesTxt.Disable() useridLbl = wx.StaticText(panel, -1, 'Choose the user \n(if different):') self.user = wx.ComboBox(panel, -1, userid, None, (150, -1), user_list, wx.CB_DROPDOWN) self.user.Bind(wx.EVT_COMBOBOX, self.comboChoice) scrubBtn = wx.Button(panel, -1, 'Scrub') self.Bind(wx.EVT_BUTTON, self.scrub, scrubBtn) closeBtn = wx.Button(panel, -1, 'Close') self.Bind(wx.EVT_BUTTON, self.close, closeBtn) # create a checklistbox widget to list all the user profiles on the machine profLbl = wx.StaticText(panel, -1, 'Please choose what profiles to remove:') profLbl.SetFont(font) self.profile_list = self.getProfileList() self.profile_cblb = wx.CheckListBox(panel, -1, size=(100, -1), choices=self.profile_list) # delete button deleteBtn = wx.Button(panel, -1, 'Delete Profiles') self.Bind(wx.EVT_BUTTON, self.deleteProfile, deleteBtn) # Main sizer to hold all the lesser sizers mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(descriptionLbl) mainSizer.Add((10,10)) # pathSizer holds the top set of widgets pathSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) pathSizer.AddGrowableCol(1) pathSizer.Add(genericPathLbl, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL) pathSizer.Add(self.path, 0, wx.EXPAND) pathSizer.Add(useridLbl, 0, wx.ALIGN_LEFT|wx.ALIGN_CENTER_VERTICAL) pathSizer.Add(self.user, 1) pathSizer.Add((10,10)) pathSizer.Add((10,10)) pathSizer.Add((10,10)) pathSizer.Add((10,10)) pathSizer.Add(optionLbl,0) pathSizer.Add((10,10)) pathSizer.Add(self.tempCkbx,0) pathSizer.Add(self.tempTxt,1) pathSizer.Add(self.tempFilesCkbx,0) pathSizer.Add(self.tempFilesTxt,1) # btnSizer (holds the scrub & close buttons) btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(scrubBtn) btnSizer.Add((20,20), 1) btnSizer.Add(closeBtn) pathSizer.Add((10,10)) pathSizer.Add(btnSizer, 1, wx.ALIGN_CENTER) # Profile Sizer (holds the check box / combobox and its delete button) profSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5) profSizer.Add(profLbl, 0) profSizer.Add((10,10)) profSizer.Add(self.profile_cblb, 0) profSizer.Add((10,10)) profSizer.Add(deleteBtn, 0) # Add the pathSizer to mainSizer mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.ALL, 10) mainSizer.Add(profSizer, 0, wx.EXPAND|wx.ALL, 10) panel.SetSizer(mainSizer) mainSizer.Fit(self.frame) mainSizer.SetSizeHints(self.frame) # initialize the thread self.iniThread() # ---- Make the app visible / OnInit requires a boolean "return" self.frame.Show(True) return True def comboChoice(self, event): ''' On choice, change the two "Temp" directory paths to match the user choice. ''' self.tempTxt.SetValue('') self.tempFilesTxt.SetValue('') new_user = event.GetString() self.tempTxt.AppendText(r'C:\Documents and Settings\%s\Local Settings\Temp' % new_user) self.tempFilesTxt.AppendText(r'C:\Documents and Settings\%s\Local Settings\Temporary Internet Files' % new_user) def scrub(self, event): ''' Delete the folders that the user has chosen. ''' folders = [] x = 0 # Used to control what gets popped tempck = self.tempCkbx.GetValue() tempFiles_ck = self.tempFilesCkbx.GetValue() user_path = self.path.GetValue() paths = [self.path, self.tempTxt, self.tempFilesTxt] if tempck == False: paths.pop(1) x = 1 if tempFiles_ck == False: if x: # if true, then there's only 2 values, so pop the 2nd paths.pop(1) else: # if false, there's 3 values, so pop the 3rd paths.pop(2) # Create a list of paths to be deleted for path in paths: if path.GetValue() != '': folders.append(path.GetValue()) # If the user doesn't enter a path and unchecks both boxes, let the user know! if tempck == False and tempFiles_ck == False and user_path == '': dlg = wx.MessageDialog(None, 'You need to enter a path or check a checkbox to delete.', 'Information', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() # Get response code resCode = self.warningMessage(folders) # Delete the folder(s) if user chose "Yes" if (resCode == wx.ID_YES): t = time.ctime() print '**************************************************' print '\nNew Scrub Job accepted %s\n' % t print '**************************************************' FDeleteThread(self, folders) def warningMessage(self, folders): ''' Create a custom warning message for the user based on his choices. ''' if len(folders) == 3: response = wx.MessageDialog(None, 'This will attempt to delete these folder\'s subfolders:\n\n%s\n%s\n%s\n\nIs this OK?' % (folders[0],folders[1],folders[2]), 'WARNING', wx.YES_NO | wx.ICON_QUESTION) resCode = response.ShowModal() response.Destroy() elif len(folders) == 2: response = wx.MessageDialog(None, 'This will attempt to delete these folder\'s subfolders:\n\n%s\n%s\n\nIs this OK?' % (folders[0],folders[1]), 'WARNING', wx.YES_NO | wx.ICON_QUESTION) resCode = response.ShowModal() response.Destroy() elif len(folders) == 1: response = wx.MessageDialog(None,'This will attempt to delete this folder\'s subfolders:\n\n%s\n\nIs this OK?' % (folders[0]), 'WARNING', wx.YES_NO | wx.ICON_QUESTION) resCode = response.ShowModal() response.Destroy() else: pass return resCode def getProfileList(self): ''' Search "Documents and Settings" folder and create a list of user profiles. ''' userPaths = glob.glob(r'c:\documents and settings\\*') user_list = [] for i in userPaths: if len(i[26:]) > 6: pass elif i[26:].upper() == userid: pass else: user_list.append(i[26:]) return user_list def deleteProfile(self, event): ''' Delete the profile(s). ''' prof_list = [] count = 0 resCode = '' prof_dir = r'C:\Documents and Settings' # Check if any profiles are checked and create a list of them. for i in self.profile_list: if self.profile_cblb.IsChecked(count): print '%s is checked' % i prof_list.append(i) else: print '%s is NOT checked' % i count += 1 # Warn user about deleting profiles if len(prof_list) > 0: response = wx.MessageDialog(None, 'Do you really want to delete these profile(s)?', 'WARNING', wx.YES_NO | wx.ICON_QUESTION) resCode = response.ShowModal() response.Destroy() # If "Yes" is chosen, delete the checked profiles if resCode == wx.ID_YES: for x in prof_list: print prof_dir + '\\%s' % x #shutil.rmtree(prof_dir + '\\%s' % x, ignore_errors=True) #os.system(r'RD /S /Q "%s"' % (prof_dir + '\\%s' % x)) subprocess.Popen(r'RD /S /Q "%s"' % (prof_dir + '\\%s' % x), shell=True) print 'Finished deleting selected profiles.' # Clear the combo-listbox self.profile_cblb.Clear() print 'cleared' self.profile_cblb.AppendItems(self.getProfileList()) def iniThread(self): ''' Initialize thread object. ''' ##### beginning of threading code setup ##### # Set up event handler for any worker thread results EVT_RESULT(self,self.onResult) # And indicate we don't have a worker thread yet self.worker = None ###### end of threading code ####### def onResult(self, event): ''' Reset the worker thread to None. ''' print 'Thread has finished!' self.worker = None def close(self, event): ''' Close the window ''' self.frame.Close(True) ##################################################################################### EVT_RESULT_ID = wx.NewId() def EVT_RESULT(win, func): """Define Result Event.""" win.Connect(-1, -1, EVT_RESULT_ID, func) class ResultEvent(wx.PyEvent): """Simple event to carry arbitrary result data.""" def __init__(self, data): """Init Result Event.""" wx.PyEvent.__init__(self) self.SetEventType(EVT_RESULT_ID) self.data = data class FDeleteThread(Thread): """Printer Child Thread Class.""" def __init__(self, notify_window, fList): """Init Worker Thread Class.""" Thread.__init__(self) self._notify_window = notify_window self._want_abort = 0 self.folders = fList self.start() # start the thread def run(self): """Run Worker Thread.""" for folder in self.folders: dirs = glob.glob(folder + '\\*') files = glob.glob(folder + '\\*.*') for d in dirs: if os.path.isdir(d): self.delete2(d) # END IF # END INNER FOR LOOP for f in files: try: os.remove(f) except: print 'ERROR - Could not remove: %s' % f print 'Scrubbing finished!' wx.PostEvent(self._notify_window, ResultEvent(10)) def delete2(self, item): if os.path.isdir(item): try: print '\nDeleting %s' % item shutil.rmtree(item) except: #os.system(r'RD /S /Q "%s"' % item) subprocess.Popen(r'RD /S /Q "%s"' % item, shell=True) else: try: os.remove(item) except: #os.system('DEL /F /S /Q "%s"' % item) subprocess.Popen(r'DEL /F /S /Q "%s"' % item, shell=True) if __name__ == '__main__': app = ProfileScrubber() app.MainLoop()
-- http://mail.python.org/mailman/listinfo/python-list