| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
… deleted EditorWindow.focus_in_event calls last_mtime on every <FocusIn> event, which passed the open file path to os.path.getmtime with no guard. When another program deleted or renamed the file, refocusing the IDLE window let a FileNotFoundError escape the Tk event handler. last_mtime now returns the last known mtime when the path is inaccessible, so refocusing no longer raises and does not prompt to reload a file that is gone.
There was a problem hiding this comment.
Thanks for the PR — the FocusIn traceback is real, but the fallback return self.mtime runs too early in one case.
EditorWindow.__init__ calls io.set_filename(filename) for a filename that does not exist yet (python -m idlelib new.py), which goes set_saved(True) → last_mtime() before self.mtime is assigned at line 315. On main that already aborts IDLE at startup with FileNotFoundError (a regression from gh-94523, shipped in 3.13.15, 3.14.7 and 3.15.0b1); with this patch it aborts with AttributeError: 'EditorWindow' object has no attribute 'mtime' instead.
last_mtime() should return None when there is no file or it cannot be stat'ed (not 0, which is a valid mtime on filesystems that do not keep timestamps). A new file then opens with mtime = None, and a file that is created by another program after opening still triggers the existing reload prompt.
When the file was there and is now gone (self.mtime is not None and mtime is None), "modified by another program, reload?" is the wrong question — reloading can only fail. Ask instead what to do with the buffer: Close, Save As or Ignore, with Save As as the default. tkinter.simpledialog.SimpleDialog(..., buttons=('Close', 'Save As', 'Ignore'), default=1, cancel=2) gives such a box; .go() returns the button index. Close → self.close(). Save As → self.io.save_as(event). Ignore → set self.mtime = None and self.set_saved(False), so the window is marked as unsaved but the question is not repeated on the next FocusIn.
This is an approximate plan; details can be changed later.
Please add a test for the not-yet-existing filename (a SimpleNamespace stub without mtime is enough to catch the AttributeError), expect None in test_deleted_file_does_not_raise, and tests for the deleted-file branch with the dialog mocked.
Two smaller things: the NEWS file belongs in Misc/NEWS.d/next/IDLE/, not Library/, and open(p, 'w').close() in the test can be Path(p).touch() or a with.
Sorry, something went wrong.
last_mtime() now returns None for a missing or un-stat-able file, which also fixes the startup path where it runs before self.mtime is set. The vanished-file case now offers Close, Save As or Ignore instead of a reload that could only fail. Tests updated; NEWS moved to IDLE/.
|
Done: last_mtime() now returns None for a missing or un-stat-able file (this also fixes the startup path where it runs before self.mtime is set), and the vanished-file case shows the Close/Save As/Ignore dialog. Tests and the NEWS (moved to IDLE/) updated. I have made the requested changes; please review again |
Sorry, something went wrong.
|
Thanks for making the requested changes! @serhiy-storchaka: please review the changes made to this pull request. |
Sorry, something went wrong.
There was a problem hiding this comment.
Thanks, this works for me now: starting with a not yet existing file, Ignore, reload after the file is recreated, Close. One remaining problem with a cancelled Save As, see the comment.
Sorry, something went wrong.
A cancelled Save As kept the old mtime and reprompted on every focus. Forget the mtime for both Save As and Ignore; a successful Save As restores it via set_saved(True). Reword the NEWS per review.
|
Fixed: the dialog now forgets the mtime for both Save As and Ignore, so a cancelled Save As no longer reprompts (a successful one restores it via set_saved(True)). Dropped the set_saved(False), applied your NEWS wording, and adjusted the tests. I have made the requested changes; please review again |
Sorry, something went wrong.
|
Thanks for making the requested changes! @serhiy-storchaka: please review the changes made to this pull request. |
Sorry, something went wrong.
There was a problem hiding this comment.
Found one more case: modify the file, delete it, get the focus back, choose Close. See the comment.
Sorry, something went wrong.
|
@tonghuaroot Serhiy requested another change 5 days ago. I would like to get this into early October releases. |
Sorry, something went wrong.
…hoices With a modified buffer, Close asks whether to save and the queued FocusIn was delivered inside that message box, reopening the dialog. Forget the mtime before showing the dialog (not only for Save As/Ignore); a successful Save As restores it via set_saved(True).
|
Done: self.mtime = None now runs before the dialog for all choices, so the queued FocusIn no longer reopens it on Close (a successful Save As still restores it). I have made the requested changes; please review again |
Sorry, something went wrong.
|
Thanks for making the requested changes! @serhiy-storchaka: please review the changes made to this pull request. |
Sorry, something went wrong.
|
For the future: It looks like you are force-pushing or something, so that each commit shows the cumulative change rather that just the little change made. For the last one, the diff should be a simple merge commit adding the one. We far prefer the latter for reviewing changes. Serhiy, ready to merge? |
Sorry, something went wrong.
There was a problem hiding this comment.
Thank you for your update.
test_deleted_file_returns_none and test_not_yet_created_filename are the same test.
Optional nits: the new tests could use os_helper.temp_dir() and os_helper.create_empty_file() instead of tempfile and pathlib.
Sorry, something went wrong.
| def test_deleted_file_returns_none(self): | ||
| with tempfile.TemporaryDirectory() as d: | ||
| p = os.path.join(d, 'gone.py') | ||
| Path(p).touch() | ||
| os.remove(p) | ||
| stub = types.SimpleNamespace(io=types.SimpleNamespace(filename=p)) | ||
| self.assertIsNone(Editor.last_mtime(stub)) | ||
|
|
There was a problem hiding this comment.
Serhiy, is simply deleting one of the two specified method what you requested?
| def test_deleted_file_returns_none(self): | |
| with tempfile.TemporaryDirectory() as d: | |
| p = os.path.join(d, 'gone.py') | |
| Path(p).touch() | |
| os.remove(p) | |
| stub = types.SimpleNamespace(io=types.SimpleNamespace(filename=p)) | |
| self.assertIsNone(Editor.last_mtime(stub)) |
I am fine with not replacing the working code.
Sorry, something went wrong.
There was a problem hiding this comment.
I'm merging as is. Tests can be edited later.
Sorry, something went wrong.
Not clear which changes this refers to. Essential changes have been done.
|
Thanks @tonghuaroot for the PR, and @terryjreedy for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13. |
Sorry, something went wrong.
|
Thanks @tonghuaroot for the PR, and @terryjreedy for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14. |
Sorry, something went wrong.
|
Thanks @tonghuaroot for the PR, and @terryjreedy for merging it 🌮🎉.. I'm working now to backport this PR to: 3.15. |
Sorry, something went wrong.
|
GH-158262 is a backport of this pull request to the 3.14 branch. |
Sorry, something went wrong.
|
GH-158263 is a backport of this pull request to the 3.13 branch. |
Sorry, something went wrong.
|
GH-158264 is a backport of this pull request to the 3.15 branch. |
Sorry, something went wrong.
…s deleted (GH-153481) (#158263) gh-153480: Stop IDLE crashing when a file open in the editor is deleted (GH-153481) EditorWindow.focus_in_event calls last_mtime on every <FocusIn> event, which passed the open file path to os.path.getmtime with no guard. When another program deleted or renamed the file, refocusing the IDLE window let a FileNotFoundError escape the Tk event handler. last_mtime now returns the last known mtime when the path is inaccessible, so refocusing no longer raises and does not prompt to reload a file that is gone. See PR discussion for more. --------- (cherry picked from commit 6af40a6) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…s deleted (GH-153481) (#158262) gh-153480: Stop IDLE crashing when a file open in the editor is deleted (GH-153481) EditorWindow.focus_in_event calls last_mtime on every <FocusIn> event, which passed the open file path to os.path.getmtime with no guard. When another program deleted or renamed the file, refocusing the IDLE window let a FileNotFoundError escape the Tk event handler. last_mtime now returns the last known mtime when the path is inaccessible, so refocusing no longer raises and does not prompt to reload a file that is gone. See PR discussion for more. --------- (cherry picked from commit 6af40a6) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
…s deleted (GH-153481) (#158264) gh-153480: Stop IDLE crashing when a file open in the editor is deleted (GH-153481) EditorWindow.focus_in_event calls last_mtime on every <FocusIn> event, which passed the open file path to os.path.getmtime with no guard. When another program deleted or renamed the file, refocusing the IDLE window let a FileNotFoundError escape the Tk event handler. last_mtime now returns the last known mtime when the path is inaccessible, so refocusing no longer raises and does not prompt to reload a file that is gone. See PR discussion for more. --------- (cherry picked from commit 6af40a6) Co-authored-by: tonghuaroot (童话) <tonghuaroot@gmail.com> Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
| Back | FazBrowse Home | New Git URL |
EditorWindow.focus_in_event runs on every <FocusIn> event and calls last_mtime, which called os.path.getmtime(self.io.filename) with no error handling. When the open file had been deleted or renamed by another program, refocusing the editor window let a FileNotFoundError escape the Tk event handler and broke the modified-on-disk reload check.
last_mtime now guards getmtime with try/except OSError and returns the last known mtime, so focus_in_event sees no change and does not prompt to reload a file that is gone. The regression test drives last_mtime as an unbound method against a duck-typed stub (no GUI) for both the deleted-file and no-filename cases.