Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(128387)

Delta Between Two Patch Sets: Lib/idlelib/IOBinding.py

Issue 15363: Idle/tkinter ~x.py 'save as' fails. closes idle
Left Patch Set: Created 7 years, 4 months ago
Right Patch Set: Created 5 years, 1 month ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 import os 1 import os
2 import types 2 import types
3 import shlex
3 import sys 4 import sys
4 import codecs 5 import codecs
5 import tempfile 6 import tempfile
6 import tkinter.filedialog as tkFileDialog 7 import tkinter.filedialog as tkFileDialog
7 import tkinter.messagebox as tkMessageBox 8 import tkinter.messagebox as tkMessageBox
8 import re 9 import re
9 from tkinter import * 10 from tkinter import *
10 from tkinter.simpledialog import askstring 11 from tkinter.simpledialog import askstring
11 12
12 from idlelib.configHandler import idleConf 13 from idlelib.configHandler import idleConf
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 locale_encoding = 'ascii' 56 locale_encoding = 'ascii'
56 codecs.lookup(locale_encoding) 57 codecs.lookup(locale_encoding)
57 except (ValueError, LookupError): 58 except (ValueError, LookupError):
58 pass 59 pass
59 60
60 locale_encoding = locale_encoding.lower() 61 locale_encoding = locale_encoding.lower()
61 62
62 encoding = locale_encoding ### KBK 07Sep07 This is used all over IDLE, check! 63 encoding = locale_encoding ### KBK 07Sep07 This is used all over IDLE, check!
63 ### 'encoding' is used below in encode(), check! 64 ### 'encoding' is used below in encode(), check!
64 65
65 coding_re = re.compile("coding[:=]\s*([-\w_.]+)") 66 coding_re = re.compile(r'^[ \t\f]*#.*coding[:=][ \t]*([-\w.]+)', re.ASCII)
67 blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII)
66 68
67 def coding_spec(data): 69 def coding_spec(data):
68 """Return the encoding declaration according to PEP 263. 70 """Return the encoding declaration according to PEP 263.
69 71
70 When checking encoded data, only the first two lines should be passed 72 When checking encoded data, only the first two lines should be passed
71 in to avoid a UnicodeDecodeError if the rest of the data is not unicode. 73 in to avoid a UnicodeDecodeError if the rest of the data is not unicode.
72 The first two lines would contain the encoding specification. 74 The first two lines would contain the encoding specification.
73 75
74 Raise a LookupError if the encoding is declared but unknown. 76 Raise a LookupError if the encoding is declared but unknown.
75 """ 77 """
76 if isinstance(data, bytes): 78 if isinstance(data, bytes):
77 # This encoding might be wrong. However, the coding 79 # This encoding might be wrong. However, the coding
78 # spec must be ASCII-only, so any non-ASCII characters 80 # spec must be ASCII-only, so any non-ASCII characters
79 # around here will be ignored. Decoding to Latin-1 should 81 # around here will be ignored. Decoding to Latin-1 should
80 # never fail (except for memory outage) 82 # never fail (except for memory outage)
81 lines = data.decode('iso-8859-1') 83 lines = data.decode('iso-8859-1')
82 else: 84 else:
83 lines = data 85 lines = data
84 # consider only the first two lines 86 # consider only the first two lines
85 if '\n' in lines: 87 if '\n' in lines:
86 lst = lines.split('\n')[:2] 88 lst = lines.split('\n', 2)[:2]
87 elif '\r' in lines: 89 elif '\r' in lines:
88 lst = lines.split('\r')[:2] 90 lst = lines.split('\r', 2)[:2]
89 else: 91 else:
90 lst = list(lines) 92 lst = [lines]
91 str = '\n'.join(lst) 93 for line in lst:
92 match = coding_re.search(str) 94 match = coding_re.match(line)
93 if not match: 95 if match is not None:
96 break
97 if not blank_re.match(line):
98 return None
99 else:
94 return None 100 return None
95 name = match.group(1) 101 name = match.group(1)
96 try: 102 try:
97 codecs.lookup(name) 103 codecs.lookup(name)
98 except LookupError: 104 except LookupError:
99 # The standard encoding error does not indicate the encoding 105 # The standard encoding error does not indicate the encoding
100 raise LookupError("Unknown encoding: "+name) 106 raise LookupError("Unknown encoding: "+name)
101 return name 107 return name
102 108
103 109
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 return "break" 206 return "break"
201 207
202 eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac) 208 eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
203 eol_re = re.compile(eol) 209 eol_re = re.compile(eol)
204 eol_convention = os.linesep # default 210 eol_convention = os.linesep # default
205 211
206 def loadfile(self, filename): 212 def loadfile(self, filename):
207 try: 213 try:
208 # open the file in binary mode so that we can handle 214 # open the file in binary mode so that we can handle
209 # end-of-line convention ourselves. 215 # end-of-line convention ourselves.
210 f = open(filename,'rb') 216 with open(filename, 'rb') as f:
211 two_lines = f.readline() + f.readline() 217 two_lines = f.readline() + f.readline()
212 f.seek(0) 218 f.seek(0)
213 bytes = f.read() 219 bytes = f.read()
214 f.close() 220 except OSError as msg:
215 except IOError as msg:
216 tkMessageBox.showerror("I/O Error", str(msg), master=self.text) 221 tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
217 return False 222 return False
218 chars, converted = self._decode(two_lines, bytes) 223 chars, converted = self._decode(two_lines, bytes)
219 if chars is None: 224 if chars is None:
220 tkMessageBox.showerror("Decoding Error", 225 tkMessageBox.showerror("Decoding Error",
221 "File %s\nFailed to Decode" % filename, 226 "File %s\nFailed to Decode" % filename,
222 parent=self.text) 227 parent=self.text)
223 return False 228 return False
224 # We now convert all end-of-lines to '\n's 229 # We now convert all end-of-lines to '\n's
225 firsteol = self.eol_re.search(chars) 230 firsteol = self.eol_re.search(chars)
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 self.updaterecentfileslist(filename) 370 self.updaterecentfileslist(filename)
366 return "break" 371 return "break"
367 372
368 def writefile(self, filename): 373 def writefile(self, filename):
369 self.fixlastline() 374 self.fixlastline()
370 text = self.text.get("1.0", "end-1c") 375 text = self.text.get("1.0", "end-1c")
371 if self.eol_convention != "\n": 376 if self.eol_convention != "\n":
372 text = text.replace("\n", self.eol_convention) 377 text = text.replace("\n", self.eol_convention)
373 chars = self.encode(text) 378 chars = self.encode(text)
374 try: 379 try:
375 f = open(filename, "wb") 380 with open(filename, "wb") as f:
376 f.write(chars) 381 f.write(chars)
377 f.flush()
378 f.close()
379 return True 382 return True
380 except IOError as msg: 383 except OSError as msg:
381 tkMessageBox.showerror("I/O Error", str(msg), 384 tkMessageBox.showerror("I/O Error", str(msg),
382 master=self.text) 385 master=self.text)
383 return False 386 return False
384 387
385 def encode(self, chars): 388 def encode(self, chars):
386 if isinstance(chars, bytes): 389 if isinstance(chars, bytes):
387 # This is either plain ASCII, or Tk was returning mixed-encoding 390 # This is either plain ASCII, or Tk was returning mixed-encoding
388 # text to us. Don't try to guess further. 391 # text to us. Don't try to guess further.
389 return chars 392 return chars
390 # Preserve a BOM that might have been present on opening 393 # Preserve a BOM that might have been present on opening
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 printPlatform = True 454 printPlatform = True
452 if platform == 'posix': #posix platform 455 if platform == 'posix': #posix platform
453 command = idleConf.GetOption('main','General', 456 command = idleConf.GetOption('main','General',
454 'print-command-posix') 457 'print-command-posix')
455 command = command + " 2>&1" 458 command = command + " 2>&1"
456 elif platform == 'nt': #win32 platform 459 elif platform == 'nt': #win32 platform
457 command = idleConf.GetOption('main','General','print-command-win') 460 command = idleConf.GetOption('main','General','print-command-win')
458 else: #no printing for this platform 461 else: #no printing for this platform
459 printPlatform = False 462 printPlatform = False
460 if printPlatform: #we can try to print for this platform 463 if printPlatform: #we can try to print for this platform
461 command = command % filename 464 command = command % shlex.quote(filename)
462 pipe = os.popen(command, "r") 465 pipe = os.popen(command, "r")
463 # things can get ugly on NT if there is no printer available. 466 # things can get ugly on NT if there is no printer available.
464 output = pipe.read().strip() 467 output = pipe.read().strip()
465 status = pipe.close() 468 status = pipe.close()
466 if status: 469 if status:
467 output = "Printing failed (exit status 0x%x)\n" % \ 470 output = "Printing failed (exit status 0x%x)\n" % \
468 status + output 471 status + output
469 if output: 472 if output:
470 output = "Printing command: %s\n" % repr(command) + output 473 output = "Printing command: %s\n" % repr(command) + output
471 tkMessageBox.showerror("Print status", output, master=self.text) 474 tkMessageBox.showerror("Print status", output, master=self.text)
(...skipping 18 matching lines...) Expand all
490 def askopenfile(self): 493 def askopenfile(self):
491 dir, base = self.defaultfilename("open") 494 dir, base = self.defaultfilename("open")
492 if not self.opendialog: 495 if not self.opendialog:
493 self.opendialog = tkFileDialog.Open(master=self.text, 496 self.opendialog = tkFileDialog.Open(master=self.text,
494 filetypes=self.filetypes) 497 filetypes=self.filetypes)
495 filename = self.opendialog.show(initialdir=dir, initialfile=base) 498 filename = self.opendialog.show(initialdir=dir, initialfile=base)
496 return filename 499 return filename
497 500
498 def defaultfilename(self, mode="open"): 501 def defaultfilename(self, mode="open"):
499 if self.filename: 502 if self.filename:
500 dir, base = os.path.split(self.filename) 503 dirname, base = os.path.split(self.filename)
storchaka 2014/10/12 11:40:28 Either dirname and basename or dir and base for co
504 if base.startswith('~') and sys.platform[:3] == 'win':
storchaka 2014/10/12 11:40:28 base.startswith('~') but sys.platform[:3] == 'win'
505 # Fix Tcl/Tk Tilde Substitution problem on Windows
506 base = './' + base # Escape the tilde
storchaka 2014/10/12 11:40:28 Or rather os.path.join(os.path.curdir, base).
507 return dirname, base
501 elif self.dirname: 508 elif self.dirname:
502 dir, base = self.dirname, "" 509 return self.dirname, ""
503 else: 510 else:
504 try: 511 try:
505 pwd = os.getcwd() 512 dirname = os.getcwd()
506 except os.error: 513 except OSError:
507 pwd = "" 514 dirname = ""
508 dir, base = pwd, "" 515 return dirname, ""
509 if base.startswith('~') and sys.platform[:3] == 'win':
510 # Fix Tcl/Tk Tilde Substitution problem on Windows
asvetlov 2012/07/20 18:57:53 I think mentioning http://www.tcl.tk/man/tcl/TclCm
511 base = '\\' + base # escape the tilde
512 return dir, base
513 516
514 def asksavefile(self): 517 def asksavefile(self):
515 dir, base = self.defaultfilename("save") 518 dir, base = self.defaultfilename("save")
516 if not self.savedialog: 519 if not self.savedialog:
517 self.savedialog = tkFileDialog.SaveAs( 520 self.savedialog = tkFileDialog.SaveAs(
518 master=self.text, 521 master=self.text,
519 filetypes=self.filetypes, 522 filetypes=self.filetypes,
520 defaultextension=self.defaultextension) 523 defaultextension=self.defaultextension)
521 filename = self.savedialog.show(initialdir=dir, initialfile=base) 524 filename = self.savedialog.show(initialdir=dir, initialfile=base)
522 return filename 525 return filename
523 526
524 def updaterecentfileslist(self,filename): 527 def updaterecentfileslist(self,filename):
525 "Update recent file list on all editor windows" 528 "Update recent file list on all editor windows"
526 if self.editwin.flist: 529 if self.editwin.flist:
527 self.editwin.update_recent_files_list(filename) 530 self.editwin.update_recent_files_list(filename)
528 531
529 def test(): 532 def _io_binding(parent):
530 root = Tk() 533 root = Tk()
534 root.title("Test IOBinding")
535 width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
536 root.geometry("+%d+%d"%(x, y + 150))
531 class MyEditWin: 537 class MyEditWin:
532 def __init__(self, text): 538 def __init__(self, text):
533 self.text = text 539 self.text = text
534 self.flist = None 540 self.flist = None
535 self.text.bind("<Control-o>", self.open) 541 self.text.bind("<Control-o>", self.open)
536 self.text.bind("<Control-s>", self.save) 542 self.text.bind("<Control-s>", self.save)
537 self.text.bind("<Alt-s>", self.save_as)
538 self.text.bind("<Alt-z>", self.save_a_copy)
539 def get_saved(self): return 0 543 def get_saved(self): return 0
540 def set_saved(self, flag): pass 544 def set_saved(self, flag): pass
541 def reset_undo(self): pass 545 def reset_undo(self): pass
542 def open(self, event): 546 def open(self, event):
543 self.text.event_generate("<<open-window-from-file>>") 547 self.text.event_generate("<<open-window-from-file>>")
544 def save(self, event): 548 def save(self, event):
545 self.text.event_generate("<<save-window>>") 549 self.text.event_generate("<<save-window>>")
546 def save_as(self, event): 550
547 self.text.event_generate("<<save-window-as-file>>")
548 def save_a_copy(self, event):
549 self.text.event_generate("<<save-copy-of-window-as-file>>")
550 text = Text(root) 551 text = Text(root)
551 text.pack() 552 text.pack()
552 text.focus_set() 553 text.focus_set()
553 editwin = MyEditWin(text) 554 editwin = MyEditWin(text)
554 io = IOBinding(editwin) 555 io = IOBinding(editwin)
555 root.mainloop()
556 556
557 if __name__ == "__main__": 557 if __name__ == "__main__":
558 test() 558 from idlelib.idle_test.htest import run
559 run(_io_binding)
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+