From 76b645124b3aaa34bc664eece43707c01ef1b382 Mon Sep 17 00:00:00 2001 From: Flavian Hautbois Date: Fri, 26 Jul 2019 03:30:33 +0200 Subject: [PATCH] bpo-29446: tkinter 'import *' only imports what it should (GH-14864) Add __all__ to tkinter.__init__ and submodules. Replace 'import *' with explicit imports in some submodules. --- Lib/tkinter/__init__.py | 6 +++++- Lib/tkinter/colorchooser.py | 2 ++ Lib/tkinter/commondialog.py | 8 +++++--- Lib/tkinter/dialog.py | 5 +++-- Lib/tkinter/dnd.py | 3 ++- Lib/tkinter/filedialog.py | 14 ++++++++++---- Lib/tkinter/font.py | 5 +++-- Lib/tkinter/messagebox.py | 4 ++++ Lib/tkinter/scrolledtext.py | 4 ++-- Lib/tkinter/test/test_tkinter/test_misc.py | 14 ++++++++++++++ Misc/ACKS | 1 + .../2019-07-19-16-06-48.bpo-29446.iXGuoi.rst | 1 + 12 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index 57d5b257282..9626a2780db 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -32,13 +32,13 @@ tk.mainloop() import enum import sys +import types import _tkinter # If this fails your Python may not be configured for Tk TclError = _tkinter.TclError from tkinter.constants import * import re - wantobjects = 1 TkVersion = float(_tkinter.TK_VERSION) @@ -4569,5 +4569,9 @@ def _test(): root.mainloop() +__all__ = [name for name, obj in globals().items() + if not name.startswith('_') and not isinstance(obj, types.ModuleType) + and name not in {'wantobjects'}] + if __name__ == '__main__': _test() diff --git a/Lib/tkinter/colorchooser.py b/Lib/tkinter/colorchooser.py index 9dc96713364..3cfc06f6f1f 100644 --- a/Lib/tkinter/colorchooser.py +++ b/Lib/tkinter/colorchooser.py @@ -21,6 +21,8 @@ from tkinter.commondialog import Dialog +__all__ = ["Chooser", "askcolor"] + # # color chooser class diff --git a/Lib/tkinter/commondialog.py b/Lib/tkinter/commondialog.py index c4ec010ee6b..e56b5baf7d1 100644 --- a/Lib/tkinter/commondialog.py +++ b/Lib/tkinter/commondialog.py @@ -8,15 +8,17 @@ # written by Fredrik Lundh, May 1997 # -from tkinter import * +__all__ = ["Dialog"] + +from tkinter import Frame class Dialog: - command = None + command = None def __init__(self, master=None, **options): - self.master = master + self.master = master self.options = options if not master and options.get('parent'): self.master = options['parent'] diff --git a/Lib/tkinter/dialog.py b/Lib/tkinter/dialog.py index cb463f717c0..8ae21401172 100644 --- a/Lib/tkinter/dialog.py +++ b/Lib/tkinter/dialog.py @@ -1,7 +1,8 @@ # dialog.py -- Tkinter interface to the tk_dialog script. -from tkinter import * -from tkinter import _cnfmerge +from tkinter import _cnfmerge, Widget, TclError, Button, Pack + +__all__ = ["Dialog"] DIALOG_ICON = 'questhead' diff --git a/Lib/tkinter/dnd.py b/Lib/tkinter/dnd.py index 4de2331c876..3120ff342f8 100644 --- a/Lib/tkinter/dnd.py +++ b/Lib/tkinter/dnd.py @@ -99,9 +99,10 @@ active; it will never call dnd_commit(). """ - import tkinter +__all__ = ["dnd_start", "DndHandler"] + # The factory function diff --git a/Lib/tkinter/filedialog.py b/Lib/tkinter/filedialog.py index d9d3436145c..dbb97dd5777 100644 --- a/Lib/tkinter/filedialog.py +++ b/Lib/tkinter/filedialog.py @@ -11,14 +11,20 @@ to the native file dialogues available in Tk 4.2 and newer, and the directory dialogue available in Tk 8.3 and newer. These interfaces were written by Fredrik Lundh, May 1997. """ +__all__ = ["FileDialog", "LoadFileDialog", "SaveFileDialog", + "Open", "SaveAs", "Directory", + "askopenfilename", "asksaveasfilename", "askopenfilenames", + "askopenfile", "askopenfiles", "asksaveasfile", "askdirectory"] -from tkinter import * +import fnmatch +import os +from tkinter import ( + Frame, LEFT, YES, BOTTOM, Entry, TOP, Button, Tk, X, + Toplevel, RIGHT, Y, END, Listbox, BOTH, Scrollbar, +) from tkinter.dialog import Dialog from tkinter import commondialog -import os -import fnmatch - dialogstates = {} diff --git a/Lib/tkinter/font.py b/Lib/tkinter/font.py index 136425726ab..eeff454b530 100644 --- a/Lib/tkinter/font.py +++ b/Lib/tkinter/font.py @@ -3,11 +3,12 @@ # written by Fredrik Lundh, February 1998 # -__version__ = "0.9" - import itertools import tkinter +__version__ = "0.9" +__all__ = ["NORMAL", "ROMAN", "BOLD", "ITALIC", + "nametofont", "Font", "families", "names"] # weight/slant NORMAL = "normal" diff --git a/Lib/tkinter/messagebox.py b/Lib/tkinter/messagebox.py index 4a711fa623b..5f0343b660c 100644 --- a/Lib/tkinter/messagebox.py +++ b/Lib/tkinter/messagebox.py @@ -24,6 +24,10 @@ from tkinter.commondialog import Dialog +__all__ = ["showinfo", "showwarning", "showerror", + "askquestion", "askokcancel", "askyesno", + "askyesnocancel", "askretrycancel"] + # # constants diff --git a/Lib/tkinter/scrolledtext.py b/Lib/tkinter/scrolledtext.py index 749a06a6f00..4f9a8815b61 100644 --- a/Lib/tkinter/scrolledtext.py +++ b/Lib/tkinter/scrolledtext.py @@ -11,11 +11,11 @@ Most methods calls are inherited from the Text widget; Pack, Grid and Place methods are redirected to the Frame widget however. """ -__all__ = ['ScrolledText'] - from tkinter import Frame, Text, Scrollbar, Pack, Grid, Place from tkinter.constants import RIGHT, LEFT, Y, BOTH +__all__ = ['ScrolledText'] + class ScrolledText(Text): def __init__(self, master=None, **kw): diff --git a/Lib/tkinter/test/test_tkinter/test_misc.py b/Lib/tkinter/test/test_tkinter/test_misc.py index 1d1a3c29f6b..9d5a93ef6fb 100644 --- a/Lib/tkinter/test/test_tkinter/test_misc.py +++ b/Lib/tkinter/test/test_tkinter/test_misc.py @@ -7,6 +7,20 @@ support.requires('gui') class MiscTest(AbstractTkTest, unittest.TestCase): + def test_all(self): + self.assertIn("Widget", tkinter.__all__) + # Check that variables from tkinter.constants are also in tkinter.__all__ + self.assertIn("CASCADE", tkinter.__all__) + self.assertIsNotNone(tkinter.CASCADE) + # Check that sys, re, and constants are not in tkinter.__all__ + self.assertNotIn("re", tkinter.__all__) + self.assertNotIn("sys", tkinter.__all__) + self.assertNotIn("constants", tkinter.__all__) + # Check that an underscored functions is not in tkinter.__all__ + self.assertNotIn("_tkerror", tkinter.__all__) + # Check that wantobjects is not in tkinter.__all__ + self.assertNotIn("wantobjects", tkinter.__all__) + def test_repr(self): t = tkinter.Toplevel(self.root, name='top') f = tkinter.Frame(t, name='child') diff --git a/Misc/ACKS b/Misc/ACKS index b062855b9e2..e02e8e1fa51 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -649,6 +649,7 @@ Zac Hatfield-Dodds Shane Hathaway Michael Haubenwallner Janko Hauser +Flavian Hautbois Rycharde Hawkes Ben Hayden Jochen Hayek diff --git a/Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst b/Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst new file mode 100644 index 00000000000..9afda7efe45 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst @@ -0,0 +1 @@ +Make `from tkinter import *` import only the expected objects. \ No newline at end of file