From 6cafdc8ff847c4c1c85043999a82626af65ac562 Mon Sep 17 00:00:00 2001 From: Zackery Spytz Date: Mon, 4 Nov 2019 17:36:32 -0700 Subject: [PATCH] bpo-25522: IDLE "Save As" warnings Give a warning when attempting to save a file that shadows a module in the standard library. --- Lib/idlelib/iomenu.py | 46 ++++++++++++++++++- .../2019-11-04-17-36-01.bpo-25522.5MyfcD.rst | 2 + 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/IDLE/2019-11-04-17-36-01.bpo-25522.5MyfcD.rst diff --git a/Lib/idlelib/iomenu.py b/Lib/idlelib/iomenu.py index b5533be79f9..1522fc25652 100644 --- a/Lib/idlelib/iomenu.py +++ b/Lib/idlelib/iomenu.py @@ -104,6 +104,21 @@ def coding_spec(data): return name +def libmodules(): + libdir = os.path.dirname(os.path.dirname(__file__)) + for entry in os.listdir(libdir): + if entry.endswith(".py"): + yield entry[:-3] + fullpath = os.path.join(libdir, entry) + if (os.path.isdir(fullpath) and + os.path.exists(os.path.join(fullpath, "__init__.py"))): + yield entry + + +islibmodule = frozenset(libmodules()).__contains__ +isbinmodule = frozenset(sys.builtin_module_names).__contains__ + + class IOBinding: # One instance per editor Window so methods know which to save, close. # Open returns focus to self.editwin if aborted. @@ -512,6 +527,11 @@ class IOBinding: pwd = "" return pwd, "" + def warnbadfilename(self, msg): + return tkMessageBox.askokcancel( + title="Bad File Name", message=msg, + default=tkMessageBox.CANCEL, parent=self.text) + def asksavefile(self): dir, base = self.defaultfilename("save") if not self.savedialog: @@ -519,8 +539,30 @@ class IOBinding: parent=self.text, filetypes=self.filetypes, defaultextension=self.defaultextension) - filename = self.savedialog.show(initialdir=dir, initialfile=base) - return filename + go = False + while not go: + filename = self.savedialog.show(initialdir=dir, initialfile=base) + if not filename: + return '' + name = os.path.splitext(os.path.basename(filename))[0] + stdlibmsg = ("Neither you nor python will be able to import the " + "stdlib module.") + if islibmodule(name): + go = self.warnbadfilename( + f"This name matches a stdlib name in the Lib " + f"directory.\n{stdlibmsg}") + elif isbinmodule(name): + go = self.warnbadfilename( + f"This name matches a builtin stdlib name.\n" + f"{stdlibmsg}") + elif not name.isidentifier(): + go = self.warnbadfilename( + "This name is not a valid identifier.\n" + "You will not be able to import this file.") + else: + go = True + if go: + return filename def updaterecentfileslist(self,filename): "Update recent file list on all editor windows" diff --git a/Misc/NEWS.d/next/IDLE/2019-11-04-17-36-01.bpo-25522.5MyfcD.rst b/Misc/NEWS.d/next/IDLE/2019-11-04-17-36-01.bpo-25522.5MyfcD.rst new file mode 100644 index 00000000000..2870b222f2e --- /dev/null +++ b/Misc/NEWS.d/next/IDLE/2019-11-04-17-36-01.bpo-25522.5MyfcD.rst @@ -0,0 +1,2 @@ +IDLE now warns when a user attempts to save a file that shadows a module in +the standard library.