[Python-checkins] bpo-29446: tkinter 'import *' only imports what it should (GH-14864)

Terry Jan Reedy webhook-mailer at python.org
Thu Jul 25 21:30:42 EDT 2019


https://github.com/python/cpython/commit/76b645124b3aaa34bc664eece43707c01ef1b382
commit: 76b645124b3aaa34bc664eece43707c01ef1b382
branch: master
author: Flavian Hautbois <flavianh at sicara.com>
committer: Terry Jan Reedy <tjreedy at udel.edu>
date: 2019-07-25T21:30:33-04:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2019-07-19-16-06-48.bpo-29446.iXGuoi.rst
M Lib/tkinter/__init__.py
M Lib/tkinter/colorchooser.py
M Lib/tkinter/commondialog.py
M Lib/tkinter/dialog.py
M Lib/tkinter/dnd.py
M Lib/tkinter/filedialog.py
M Lib/tkinter/font.py
M Lib/tkinter/messagebox.py
M Lib/tkinter/scrolledtext.py
M Lib/tkinter/test/test_tkinter/test_misc.py
M Misc/ACKS

diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py
index 57d5b2572822..9626a2780db6 100644
--- a/Lib/tkinter/__init__.py
+++ b/Lib/tkinter/__init__.py
@@ -32,13 +32,13 @@
 
 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 9dc967133640..3cfc06f6f1fa 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 c4ec010ee6b3..e56b5baf7d1e 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 cb463f717c0e..8ae214011727 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 4de2331c8762..3120ff342f8c 100644
--- a/Lib/tkinter/dnd.py
+++ b/Lib/tkinter/dnd.py
@@ -99,9 +99,10 @@
 
 """
 
-
 import tkinter
 
+__all__ = ["dnd_start", "DndHandler"]
+
 
 # The factory function
 
diff --git a/Lib/tkinter/filedialog.py b/Lib/tkinter/filedialog.py
index d9d3436145c9..dbb97dd5777e 100644
--- a/Lib/tkinter/filedialog.py
+++ b/Lib/tkinter/filedialog.py
@@ -11,14 +11,20 @@
 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 136425726ab8..eeff454b5306 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 4a711fa623b3..5f0343b660c6 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 749a06a6f00f..4f9a8815b618 100644
--- a/Lib/tkinter/scrolledtext.py
+++ b/Lib/tkinter/scrolledtext.py
@@ -11,11 +11,11 @@
 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 1d1a3c29f6bc..9d5a93ef6fb3 100644
--- a/Lib/tkinter/test/test_tkinter/test_misc.py
+++ b/Lib/tkinter/test/test_tkinter/test_misc.py
@@ -7,6 +7,20 @@
 
 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 b062855b9e28..e02e8e1fa515 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 000000000000..9afda7efe451
--- /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



More information about the Python-checkins mailing list