openmedialibrary_platform_d.../lib/python3.7/idlelib/iomenu.py

575 lines
20 KiB
Python
Raw Normal View History

2018-12-31 23:25:26 +00:00
import codecs
from codecs import BOM_UTF8
2016-02-06 09:36:57 +00:00
import os
2018-12-31 23:25:26 +00:00
import re
2016-02-06 09:36:57 +00:00
import shlex
import sys
import tempfile
2018-12-31 23:25:26 +00:00
2016-02-06 09:36:57 +00:00
import tkinter.filedialog as tkFileDialog
import tkinter.messagebox as tkMessageBox
from tkinter.simpledialog import askstring
2018-12-31 23:25:26 +00:00
import idlelib
from idlelib.config import idleConf
2016-02-06 09:36:57 +00:00
2018-12-31 23:25:26 +00:00
if idlelib.testing: # Set True by test.test_idle to avoid setlocale.
encoding = 'utf-8'
2016-02-06 09:36:57 +00:00
else:
2018-12-31 23:25:26 +00:00
# Try setting the locale, so that we can find out
# what encoding to use
2016-02-06 09:36:57 +00:00
try:
2018-12-31 23:25:26 +00:00
import locale
locale.setlocale(locale.LC_CTYPE, "")
except (ImportError, locale.Error):
pass
locale_decode = 'ascii'
if sys.platform == 'win32':
# On Windows, we could use "mbcs". However, to give the user
# a portable encoding name, we need to find the code page
2016-02-06 09:36:57 +00:00
try:
locale_encoding = locale.getdefaultlocale()[1]
codecs.lookup(locale_encoding)
2018-12-31 23:25:26 +00:00
except LookupError:
2016-02-06 09:36:57 +00:00
pass
2018-12-31 23:25:26 +00:00
else:
try:
# Different things can fail here: the locale module may not be
# loaded, it may not offer nl_langinfo, or CODESET, or the
# resulting codeset may be unknown to Python. We ignore all
# these problems, falling back to ASCII
locale_encoding = locale.nl_langinfo(locale.CODESET)
if locale_encoding is None or locale_encoding == '':
# situation occurs on macOS
locale_encoding = 'ascii'
codecs.lookup(locale_encoding)
except (NameError, AttributeError, LookupError):
# Try getdefaultlocale: it parses environment variables,
# which may give a clue. Unfortunately, getdefaultlocale has
# bugs that can cause ValueError.
try:
locale_encoding = locale.getdefaultlocale()[1]
if locale_encoding is None or locale_encoding == '':
# situation occurs on macOS
locale_encoding = 'ascii'
codecs.lookup(locale_encoding)
except (ValueError, LookupError):
pass
2016-02-06 09:36:57 +00:00
2018-12-31 23:25:26 +00:00
locale_encoding = locale_encoding.lower()
2016-02-06 09:36:57 +00:00
2018-12-31 23:25:26 +00:00
encoding = locale_encoding
# Encoding is used in multiple files; locale_encoding nowhere.
# The only use of 'encoding' below is in _decode as initial value
# of deprecated block asking user for encoding.
# Perhaps use elsewhere should be reviewed.
2016-02-06 09:36:57 +00:00
2018-12-31 23:25:26 +00:00
coding_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII)
2016-02-06 09:36:57 +00:00
blank_re = re.compile(r'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII)
def coding_spec(data):
"""Return the encoding declaration according to PEP 263.
When checking encoded data, only the first two lines should be passed
in to avoid a UnicodeDecodeError if the rest of the data is not unicode.
The first two lines would contain the encoding specification.
Raise a LookupError if the encoding is declared but unknown.
"""
if isinstance(data, bytes):
# This encoding might be wrong. However, the coding
# spec must be ASCII-only, so any non-ASCII characters
# around here will be ignored. Decoding to Latin-1 should
# never fail (except for memory outage)
lines = data.decode('iso-8859-1')
else:
lines = data
# consider only the first two lines
if '\n' in lines:
lst = lines.split('\n', 2)[:2]
elif '\r' in lines:
lst = lines.split('\r', 2)[:2]
else:
lst = [lines]
for line in lst:
match = coding_re.match(line)
if match is not None:
break
if not blank_re.match(line):
return None
else:
return None
name = match.group(1)
try:
codecs.lookup(name)
except LookupError:
# The standard encoding error does not indicate the encoding
raise LookupError("Unknown encoding: "+name)
return name
class IOBinding:
2018-12-31 23:25:26 +00:00
# One instance per editor Window so methods know which to save, close.
# Open returns focus to self.editwin if aborted.
# EditorWindow.open_module, others, belong here.
2016-02-06 09:36:57 +00:00
def __init__(self, editwin):
self.editwin = editwin
self.text = editwin.text
self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
self.__id_save = self.text.bind("<<save-window>>", self.save)
self.__id_saveas = self.text.bind("<<save-window-as-file>>",
self.save_as)
self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
self.save_a_copy)
self.fileencoding = None
self.__id_print = self.text.bind("<<print-window>>", self.print_window)
def close(self):
# Undo command bindings
self.text.unbind("<<open-window-from-file>>", self.__id_open)
self.text.unbind("<<save-window>>", self.__id_save)
self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
self.text.unbind("<<print-window>>", self.__id_print)
# 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
dirname = None
def set_filename(self, filename):
if filename and os.path.isdir(filename):
self.filename = None
self.dirname = filename
else:
self.filename = filename
self.dirname = None
self.set_saved(1)
if self.filename_change_hook:
self.filename_change_hook()
def open(self, event=None, editFile=None):
flist = self.editwin.flist
# Save in case parent window is closed (ie, during askopenfile()).
if flist:
if not editFile:
filename = self.askopenfile()
else:
filename=editFile
if filename:
# If editFile is valid and already open, flist.open will
# shift focus to its existing window.
# If the current window exists and is a fresh unnamed,
# unmodified editor window (not an interpreter shell),
# pass self.loadfile to flist.open so it will load the file
# in the current window (if the file is not already open)
# instead of a new window.
if (self.editwin and
not getattr(self.editwin, 'interp', None) and
not self.filename and
self.get_saved()):
flist.open(filename, self.loadfile)
else:
flist.open(filename)
else:
if self.text:
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"
if not editFile:
filename = self.askopenfile()
else:
filename=editFile
if filename:
self.loadfile(filename)
else:
self.text.focus_set()
return "break"
eol = r"(\r\n)|\n|\r" # \r\n (Windows), \n (UNIX), or \r (Mac)
eol_re = re.compile(eol)
eol_convention = os.linesep # default
def loadfile(self, filename):
try:
# open the file in binary mode so that we can handle
# end-of-line convention ourselves.
with open(filename, 'rb') as f:
two_lines = f.readline() + f.readline()
f.seek(0)
bytes = f.read()
except OSError as msg:
2018-12-31 23:25:26 +00:00
tkMessageBox.showerror("I/O Error", str(msg), parent=self.text)
2016-02-06 09:36:57 +00:00
return False
chars, converted = self._decode(two_lines, bytes)
if chars is None:
tkMessageBox.showerror("Decoding Error",
"File %s\nFailed to Decode" % filename,
parent=self.text)
return False
# We now convert all end-of-lines to '\n's
firsteol = self.eol_re.search(chars)
if firsteol:
self.eol_convention = firsteol.group(0)
chars = self.eol_re.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)
if converted:
# We need to save the conversion results first
# before being able to execute the code
self.set_saved(False)
self.text.mark_set("insert", "1.0")
self.text.yview("insert")
self.updaterecentfileslist(filename)
return True
def _decode(self, two_lines, bytes):
"Create a Unicode string."
chars = None
# Check presence of a UTF-8 signature first
if bytes.startswith(BOM_UTF8):
try:
chars = bytes[3:].decode("utf-8")
except UnicodeDecodeError:
# has UTF-8 signature, but fails to decode...
return None, False
else:
# Indicates that this file originally had a BOM
self.fileencoding = 'BOM'
return chars, False
# Next look for coding specification
try:
enc = coding_spec(two_lines)
except LookupError as name:
tkMessageBox.showerror(
title="Error loading the file",
message="The encoding '%s' is not known to this Python "\
"installation. The file may not display correctly" % name,
2018-12-31 23:25:26 +00:00
parent = self.text)
2016-02-06 09:36:57 +00:00
enc = None
except UnicodeDecodeError:
return None, False
if enc:
try:
chars = str(bytes, enc)
self.fileencoding = enc
return chars, False
except UnicodeDecodeError:
pass
# Try ascii:
try:
chars = str(bytes, 'ascii')
self.fileencoding = None
return chars, False
except UnicodeDecodeError:
pass
# Try utf-8:
try:
chars = str(bytes, 'utf-8')
self.fileencoding = 'utf-8'
return chars, False
except UnicodeDecodeError:
pass
# Finally, try the locale's encoding. This is deprecated;
# the user should declare a non-ASCII encoding
try:
# Wait for the editor window to appear
self.editwin.text.update()
enc = askstring(
"Specify file encoding",
"The file's encoding is invalid for Python 3.x.\n"
"IDLE will convert it to UTF-8.\n"
"What is the current encoding of the file?",
2018-12-31 23:25:26 +00:00
initialvalue = encoding,
2016-02-06 09:36:57 +00:00
parent = self.editwin.text)
if enc:
chars = str(bytes, enc)
self.fileencoding = None
return chars, True
except (UnicodeDecodeError, LookupError):
pass
return None, False # None on failure
def maybesave(self):
if self.get_saved():
return "yes"
message = "Do you want to save %s before closing?" % (
self.filename or "this untitled document")
confirm = tkMessageBox.askyesnocancel(
title="Save On Close",
message=message,
default=tkMessageBox.YES,
2018-12-31 23:25:26 +00:00
parent=self.text)
2016-02-06 09:36:57 +00:00
if confirm:
reply = "yes"
self.save(None)
if not self.get_saved():
reply = "cancel"
elif confirm is None:
reply = "cancel"
else:
reply = "no"
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(True)
try:
self.editwin.store_file_breaks()
except AttributeError: # may be a PyShell
pass
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)
try:
self.editwin.store_file_breaks()
except AttributeError:
pass
self.text.focus_set()
self.updaterecentfileslist(filename)
return "break"
def save_a_copy(self, event):
filename = self.asksavefile()
if filename:
self.writefile(filename)
self.text.focus_set()
self.updaterecentfileslist(filename)
return "break"
def writefile(self, filename):
self.fixlastline()
text = self.text.get("1.0", "end-1c")
if self.eol_convention != "\n":
text = text.replace("\n", self.eol_convention)
chars = self.encode(text)
try:
with open(filename, "wb") as f:
f.write(chars)
return True
except OSError as msg:
tkMessageBox.showerror("I/O Error", str(msg),
2018-12-31 23:25:26 +00:00
parent=self.text)
2016-02-06 09:36:57 +00:00
return False
def encode(self, chars):
if isinstance(chars, bytes):
# This is either plain ASCII, or Tk was returning mixed-encoding
# text to us. Don't try to guess further.
return chars
# Preserve a BOM that might have been present on opening
if self.fileencoding == 'BOM':
return BOM_UTF8 + chars.encode("utf-8")
# See whether there is anything non-ASCII in it.
# If not, no need to figure out the encoding.
try:
return chars.encode('ascii')
except UnicodeError:
pass
# Check if there is an encoding declared
try:
# a string, let coding_spec slice it to the first two lines
enc = coding_spec(chars)
failed = None
except LookupError as msg:
failed = msg
enc = None
else:
if not enc:
# PEP 3120: default source encoding is UTF-8
enc = 'utf-8'
if enc:
try:
return chars.encode(enc)
except UnicodeError:
failed = "Invalid encoding '%s'" % enc
tkMessageBox.showerror(
"I/O Error",
"%s.\nSaving as UTF-8" % failed,
2018-12-31 23:25:26 +00:00
parent = self.text)
2016-02-06 09:36:57 +00:00
# Fallback: save as UTF-8, with BOM - ignoring the incorrect
# declared encoding
return BOM_UTF8 + chars.encode("utf-8")
def fixlastline(self):
c = self.text.get("end-2c")
if c != '\n':
self.text.insert("end-1c", "\n")
def print_window(self, event):
confirm = tkMessageBox.askokcancel(
title="Print",
message="Print to Default Printer",
default=tkMessageBox.OK,
2018-12-31 23:25:26 +00:00
parent=self.text)
2016-02-06 09:36:57 +00:00
if not confirm:
self.text.focus_set()
return "break"
tempfilename = None
saved = self.get_saved()
if saved:
filename = self.filename
# shell undo is reset after every prompt, looks saved, probably isn't
if not saved or filename is None:
(tfd, tempfilename) = tempfile.mkstemp(prefix='IDLE_tmp_')
filename = tempfilename
os.close(tfd)
if not self.writefile(tempfilename):
os.unlink(tempfilename)
return "break"
platform = os.name
printPlatform = True
if platform == 'posix': #posix platform
command = idleConf.GetOption('main','General',
'print-command-posix')
command = command + " 2>&1"
elif platform == 'nt': #win32 platform
command = idleConf.GetOption('main','General','print-command-win')
else: #no printing for this platform
printPlatform = False
if printPlatform: #we can try to print for this platform
command = command % shlex.quote(filename)
pipe = os.popen(command, "r")
# things can get ugly on NT if there is no printer available.
output = pipe.read().strip()
status = pipe.close()
if status:
output = "Printing failed (exit status 0x%x)\n" % \
status + output
if output:
output = "Printing command: %s\n" % repr(command) + output
2018-12-31 23:25:26 +00:00
tkMessageBox.showerror("Print status", output, parent=self.text)
2016-02-06 09:36:57 +00:00
else: #no printing for this platform
message = "Printing is not enabled for this platform: %s" % platform
2018-12-31 23:25:26 +00:00
tkMessageBox.showinfo("Print status", message, parent=self.text)
2016-02-06 09:36:57 +00:00
if tempfilename:
os.unlink(tempfilename)
return "break"
opendialog = None
savedialog = None
2018-12-31 23:25:26 +00:00
filetypes = (
2016-02-06 09:36:57 +00:00
("Python files", "*.py *.pyw", "TEXT"),
("Text files", "*.txt", "TEXT"),
("All files", "*"),
2018-12-31 23:25:26 +00:00
)
2016-02-06 09:36:57 +00:00
defaultextension = '.py' if sys.platform == 'darwin' else ''
def askopenfile(self):
dir, base = self.defaultfilename("open")
if not self.opendialog:
2018-12-31 23:25:26 +00:00
self.opendialog = tkFileDialog.Open(parent=self.text,
2016-02-06 09:36:57 +00:00
filetypes=self.filetypes)
filename = self.opendialog.show(initialdir=dir, initialfile=base)
return filename
def defaultfilename(self, mode="open"):
if self.filename:
return os.path.split(self.filename)
elif self.dirname:
return self.dirname, ""
else:
try:
pwd = os.getcwd()
except OSError:
pwd = ""
return pwd, ""
def asksavefile(self):
dir, base = self.defaultfilename("save")
if not self.savedialog:
self.savedialog = tkFileDialog.SaveAs(
2018-12-31 23:25:26 +00:00
parent=self.text,
2016-02-06 09:36:57 +00:00
filetypes=self.filetypes,
defaultextension=self.defaultextension)
filename = self.savedialog.show(initialdir=dir, initialfile=base)
return filename
def updaterecentfileslist(self,filename):
"Update recent file list on all editor windows"
if self.editwin.flist:
self.editwin.update_recent_files_list(filename)
def _io_binding(parent): # htest #
2018-12-31 23:25:26 +00:00
from tkinter import Toplevel, Text
root = Toplevel(parent)
2016-02-06 09:36:57 +00:00
root.title("Test IOBinding")
2018-12-31 23:25:26 +00:00
x, y = map(int, parent.geometry().split('+')[1:])
root.geometry("+%d+%d" % (x, y + 175))
2016-02-06 09:36:57 +00:00
class MyEditWin:
def __init__(self, text):
self.text = text
self.flist = None
self.text.bind("<Control-o>", self.open)
2018-12-31 23:25:26 +00:00
self.text.bind('<Control-p>', self.print)
2016-02-06 09:36:57 +00:00
self.text.bind("<Control-s>", self.save)
2018-12-31 23:25:26 +00:00
self.text.bind("<Alt-s>", self.saveas)
self.text.bind('<Control-c>', self.savecopy)
2016-02-06 09:36:57 +00:00
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("<<open-window-from-file>>")
2018-12-31 23:25:26 +00:00
def print(self, event):
self.text.event_generate("<<print-window>>")
2016-02-06 09:36:57 +00:00
def save(self, event):
self.text.event_generate("<<save-window>>")
2018-12-31 23:25:26 +00:00
def saveas(self, event):
self.text.event_generate("<<save-window-as-file>>")
def savecopy(self, event):
self.text.event_generate("<<save-copy-of-window-as-file>>")
2016-02-06 09:36:57 +00:00
text = Text(root)
text.pack()
text.focus_set()
editwin = MyEditWin(text)
IOBinding(editwin)
if __name__ == "__main__":
2018-12-31 23:25:26 +00:00
from unittest import main
main('idlelib.idle_test.test_iomenu', verbosity=2, exit=False)
2016-02-06 09:36:57 +00:00
from idlelib.idle_test.htest import run
run(_io_binding)