mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
Merge 885f2cfdf8 into 7099af8f5e
This commit is contained in:
commit
392819cd6e
5 changed files with 134 additions and 9 deletions
|
|
@ -525,6 +525,14 @@ http.cookies
|
||||||
(Contributed by Nick Burns and Senthil Kumaran in :gh:`92936`.)
|
(Contributed by Nick Burns and Senthil Kumaran in :gh:`92936`.)
|
||||||
|
|
||||||
|
|
||||||
|
idlelib
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Add a "Reload from Disk" item to the File menu. This allows discarding
|
||||||
|
unsaved changes and reloading the current version of the file from the disk.
|
||||||
|
(Contributed by Shamil Abdulaev in :gh:`44968`.)
|
||||||
|
|
||||||
|
|
||||||
inspect
|
inspect
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
"Test , coverage 17%."
|
import builtins
|
||||||
|
import os
|
||||||
from idlelib import iomenu
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from test.support import requires
|
from test.support import requires
|
||||||
from tkinter import Tk
|
from tkinter import Tk
|
||||||
|
|
||||||
|
from idlelib import iomenu, util
|
||||||
from idlelib.editor import EditorWindow
|
from idlelib.editor import EditorWindow
|
||||||
from idlelib import util
|
|
||||||
from idlelib.idle_test.mock_idle import Func
|
from idlelib.idle_test.mock_idle import Func
|
||||||
|
|
||||||
# Fail if either tokenize.open and t.detect_encoding does not exist.
|
# Fail if either tokenize.open and t.detect_encoding does not exist.
|
||||||
# These are used in loadfile and encode.
|
# These are used in loadfile and encode.
|
||||||
# Also used in pyshell.MI.execfile and runscript.tabnanny.
|
# Also used in pyshell.MI.execfile and runscript.tabnanny.
|
||||||
from tokenize import open, detect_encoding
|
from tokenize import open as tokenize_open, detect_encoding
|
||||||
# Remove when we have proper tests that use both.
|
# Remove when we have proper tests that use both.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -36,6 +39,14 @@ def tearDownClass(cls):
|
||||||
cls.root.destroy()
|
cls.root.destroy()
|
||||||
del cls.root
|
del cls.root
|
||||||
|
|
||||||
|
def _create_tempfile(self, content: str) -> str:
|
||||||
|
fd, filename = tempfile.mkstemp(suffix='.py')
|
||||||
|
os.close(fd)
|
||||||
|
self.addCleanup(os.unlink, filename)
|
||||||
|
with builtins.open(filename, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
return filename
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
self.assertIs(self.io.editwin, self.editwin)
|
self.assertIs(self.io.editwin, self.editwin)
|
||||||
|
|
||||||
|
|
@ -45,17 +56,88 @@ def test_fixnewlines_end(self):
|
||||||
fix = io.fixnewlines
|
fix = io.fixnewlines
|
||||||
text = io.editwin.text
|
text = io.editwin.text
|
||||||
|
|
||||||
# Make the editor temporarily look like Shell.
|
|
||||||
self.editwin.interp = None
|
self.editwin.interp = None
|
||||||
shelltext = '>>> if 1'
|
shelltext = '>>> if 1'
|
||||||
self.editwin.get_prompt_text = Func(result=shelltext)
|
self.editwin.get_prompt_text = Func(result=shelltext)
|
||||||
eq(fix(), shelltext) # Get... call and '\n' not added.
|
eq(fix(), shelltext) # Get... call and '\n' not added.
|
||||||
del self.editwin.interp, self.editwin.get_prompt_text
|
del self.editwin.interp, self.editwin.get_prompt_text
|
||||||
|
|
||||||
text.insert(1.0, 'a')
|
text.insert(1.0, 'a')
|
||||||
eq(fix(), 'a'+io.eol_convention)
|
eq(fix(), 'a' + io.eol_convention)
|
||||||
eq(text.get('1.0', 'end-1c'), 'a\n')
|
eq(text.get('1.0', 'end-1c'), 'a\n')
|
||||||
eq(fix(), 'a'+io.eol_convention)
|
eq(fix(), 'a' + io.eol_convention)
|
||||||
|
|
||||||
|
def test_reload_no_file(self):
|
||||||
|
io = self.io
|
||||||
|
io.filename = None
|
||||||
|
|
||||||
|
with patch('idlelib.iomenu.messagebox.showinfo') as mock_showinfo:
|
||||||
|
result = io.reload(None)
|
||||||
|
self.assertEqual(result, "break")
|
||||||
|
mock_showinfo.assert_called_once()
|
||||||
|
args, kwargs = mock_showinfo.call_args
|
||||||
|
self.assertIn("File Not Found", args[0])
|
||||||
|
|
||||||
|
def test_reload_with_file(self):
|
||||||
|
io = self.io
|
||||||
|
text = io.editwin.text
|
||||||
|
original_content = "# Original content\n"
|
||||||
|
modified_content = "# Modified content\n"
|
||||||
|
|
||||||
|
filename = self._create_tempfile(original_content)
|
||||||
|
io.filename = filename
|
||||||
|
|
||||||
|
with patch('idlelib.iomenu.messagebox.showerror') as mock_showerror:
|
||||||
|
io.loadfile(io.filename)
|
||||||
|
self.assertEqual(text.get('1.0', 'end-1c'), original_content)
|
||||||
|
|
||||||
|
with builtins.open(filename, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(modified_content)
|
||||||
|
|
||||||
|
result = io.reload(None)
|
||||||
|
|
||||||
|
mock_showerror.assert_not_called()
|
||||||
|
self.assertEqual(result, "break")
|
||||||
|
self.assertEqual(text.get('1.0', 'end-1c'), modified_content)
|
||||||
|
|
||||||
|
def test_reload_with_unsaved_changes_cancel(self):
|
||||||
|
io = self.io
|
||||||
|
text = io.editwin.text
|
||||||
|
original_content = "# Original content\n"
|
||||||
|
unsaved_content = original_content + "\n# Unsaved change"
|
||||||
|
|
||||||
|
filename = self._create_tempfile(original_content)
|
||||||
|
io.filename = filename
|
||||||
|
io.loadfile(io.filename)
|
||||||
|
|
||||||
|
text.insert('end', "\n# Unsaved change")
|
||||||
|
io.set_saved(False)
|
||||||
|
|
||||||
|
with patch('idlelib.iomenu.messagebox.askokcancel', return_value=False) as mock_ask:
|
||||||
|
result = io.reload(None)
|
||||||
|
|
||||||
|
self.assertEqual(result, "break")
|
||||||
|
self.assertEqual(text.get('1.0', 'end-1c'), unsaved_content)
|
||||||
|
mock_ask.assert_called_once()
|
||||||
|
|
||||||
|
def test_reload_with_unsaved_changes_confirm(self):
|
||||||
|
io = self.io
|
||||||
|
text = io.editwin.text
|
||||||
|
original_content = "# Original content\n"
|
||||||
|
|
||||||
|
filename = self._create_tempfile(original_content)
|
||||||
|
io.filename = filename
|
||||||
|
io.loadfile(io.filename)
|
||||||
|
|
||||||
|
text.insert('end', "\n# Unsaved change")
|
||||||
|
io.set_saved(False)
|
||||||
|
|
||||||
|
with patch('idlelib.iomenu.messagebox.askokcancel', return_value=True) as mock_ask:
|
||||||
|
result = io.reload(None)
|
||||||
|
|
||||||
|
self.assertEqual(result, "break")
|
||||||
|
self.assertEqual(text.get('1.0', 'end-1c'), original_content)
|
||||||
|
mock_ask.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def _extension_in_filetypes(extension):
|
def _extension_in_filetypes(extension):
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ def __init__(self, editwin):
|
||||||
self.save_as)
|
self.save_as)
|
||||||
self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
|
self.__id_savecopy = self.text.bind("<<save-copy-of-window-as-file>>",
|
||||||
self.save_a_copy)
|
self.save_a_copy)
|
||||||
|
self.__id_reload = self.text.bind("<<reload-window>>", self.reload)
|
||||||
self.fileencoding = 'utf-8'
|
self.fileencoding = 'utf-8'
|
||||||
self.__id_print = self.text.bind("<<print-window>>", self.print_window)
|
self.__id_print = self.text.bind("<<print-window>>", self.print_window)
|
||||||
|
|
||||||
|
|
@ -40,6 +41,7 @@ def close(self):
|
||||||
self.text.unbind("<<save-window>>", self.__id_save)
|
self.text.unbind("<<save-window>>", self.__id_save)
|
||||||
self.text.unbind("<<save-window-as-file>>",self.__id_saveas)
|
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("<<save-copy-of-window-as-file>>", self.__id_savecopy)
|
||||||
|
self.text.unbind("<<reload-window>>", self.__id_reload)
|
||||||
self.text.unbind("<<print-window>>", self.__id_print)
|
self.text.unbind("<<print-window>>", self.__id_print)
|
||||||
# Break cycles
|
# Break cycles
|
||||||
self.editwin = None
|
self.editwin = None
|
||||||
|
|
@ -237,6 +239,35 @@ def save_a_copy(self, event):
|
||||||
self.updaterecentfileslist(filename)
|
self.updaterecentfileslist(filename)
|
||||||
return "break"
|
return "break"
|
||||||
|
|
||||||
|
def reload(self, event):
|
||||||
|
"""Reload the file from disk, discarding any unsaved changes.
|
||||||
|
|
||||||
|
If the file has unsaved changes, ask the user to confirm.
|
||||||
|
"""
|
||||||
|
if not self.filename:
|
||||||
|
messagebox.showinfo(
|
||||||
|
"File Not Found",
|
||||||
|
"This window has no associated file to reload.",
|
||||||
|
parent=self.text)
|
||||||
|
self.text.focus_set()
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
if not self.get_saved():
|
||||||
|
confirm = messagebox.askokcancel(
|
||||||
|
title="Reload File",
|
||||||
|
message=f"Discard changes to {self.filename}?",
|
||||||
|
default=messagebox.CANCEL,
|
||||||
|
parent=self.text)
|
||||||
|
if not confirm:
|
||||||
|
self.text.focus_set()
|
||||||
|
return "break"
|
||||||
|
|
||||||
|
# Reload the file
|
||||||
|
self.loadfile(self.filename)
|
||||||
|
|
||||||
|
self.text.focus_set()
|
||||||
|
return "break"
|
||||||
|
|
||||||
def writefile(self, filename):
|
def writefile(self, filename):
|
||||||
text = self.fixnewlines()
|
text = self.fixnewlines()
|
||||||
chars = self.encode(text)
|
chars = self.encode(text)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
('_Save', '<<save-window>>'),
|
('_Save', '<<save-window>>'),
|
||||||
('Save _As...', '<<save-window-as-file>>'),
|
('Save _As...', '<<save-window-as-file>>'),
|
||||||
('Save Cop_y As...', '<<save-copy-of-window-as-file>>'),
|
('Save Cop_y As...', '<<save-copy-of-window-as-file>>'),
|
||||||
|
('_Reload from Disk', '<<reload-window>>'),
|
||||||
None,
|
None,
|
||||||
('Prin_t Window', '<<print-window>>'),
|
('Prin_t Window', '<<print-window>>'),
|
||||||
None,
|
None,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add "Reload from Disk" menu item to IDLE's File menu. This allows users to
|
||||||
|
easily reload a file from disk, discarding any unsaved changes in the editor.
|
||||||
|
Patch by Shamil Abdulaev.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue