mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	Issue #18604: Consolidated checks for GUI availability.
test_support._is_gui_available is now defined the same way on every platform, and now includes the Windows-specific check that had been in the Windows version of _is_gui_available and the OSX-specific check that was in tkinter.test.support.check_tk_availability. Also, every platform checks whether Tk can be instantiated (if the platform-specific checks passed).
This commit is contained in:
		
							parent
							
								
									3d5c9e2c67
								
							
						
					
					
						commit
						ceced6bfea
					
				
					 6 changed files with 64 additions and 73 deletions
				
			
		| 
						 | 
					@ -378,12 +378,16 @@ def forget(modname):
 | 
				
			||||||
        unlink(importlib.util.cache_from_source(source, debug_override=True))
 | 
					        unlink(importlib.util.cache_from_source(source, debug_override=True))
 | 
				
			||||||
        unlink(importlib.util.cache_from_source(source, debug_override=False))
 | 
					        unlink(importlib.util.cache_from_source(source, debug_override=False))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# On some platforms, should not run gui test even if it is allowed
 | 
					# Check whether a gui is actually available
 | 
				
			||||||
# in `use_resources'.
 | 
					def _is_gui_available():
 | 
				
			||||||
if sys.platform.startswith('win'):
 | 
					    if hasattr(_is_gui_available, 'result'):
 | 
				
			||||||
 | 
					        return _is_gui_available.result
 | 
				
			||||||
 | 
					    reason = None
 | 
				
			||||||
 | 
					    if sys.platform.startswith('win'):
 | 
				
			||||||
 | 
					        # if Python is running as a service (such as the buildbot service),
 | 
				
			||||||
 | 
					        # gui interaction may be disallowed
 | 
				
			||||||
        import ctypes
 | 
					        import ctypes
 | 
				
			||||||
        import ctypes.wintypes
 | 
					        import ctypes.wintypes
 | 
				
			||||||
    def _is_gui_available():
 | 
					 | 
				
			||||||
        UOI_FLAGS = 1
 | 
					        UOI_FLAGS = 1
 | 
				
			||||||
        WSF_VISIBLE = 0x0001
 | 
					        WSF_VISIBLE = 0x0001
 | 
				
			||||||
        class USEROBJECTFLAGS(ctypes.Structure):
 | 
					        class USEROBJECTFLAGS(ctypes.Structure):
 | 
				
			||||||
| 
						 | 
					@ -403,10 +407,49 @@ class USEROBJECTFLAGS(ctypes.Structure):
 | 
				
			||||||
            ctypes.byref(needed))
 | 
					            ctypes.byref(needed))
 | 
				
			||||||
        if not res:
 | 
					        if not res:
 | 
				
			||||||
            raise ctypes.WinError()
 | 
					            raise ctypes.WinError()
 | 
				
			||||||
        return bool(uof.dwFlags & WSF_VISIBLE)
 | 
					        if not bool(uof.dwFlags & WSF_VISIBLE):
 | 
				
			||||||
else:
 | 
					            reason = "gui not available (WSF_VISIBLE flag not set)"
 | 
				
			||||||
    def _is_gui_available():
 | 
					    elif sys.platform == 'darwin':
 | 
				
			||||||
        return True
 | 
					        # The Aqua Tk implementations on OS X can abort the process if
 | 
				
			||||||
 | 
					        # being called in an environment where a window server connection
 | 
				
			||||||
 | 
					        # cannot be made, for instance when invoked by a buildbot or ssh
 | 
				
			||||||
 | 
					        # process not running under the same user id as the current console
 | 
				
			||||||
 | 
					        # user.  To avoid that, raise an exception if the window manager
 | 
				
			||||||
 | 
					        # connection is not available.
 | 
				
			||||||
 | 
					        from ctypes import cdll, c_int, pointer, Structure
 | 
				
			||||||
 | 
					        from ctypes.util import find_library
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app_services = cdll.LoadLibrary(find_library("ApplicationServices"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if app_services.CGMainDisplayID() == 0:
 | 
				
			||||||
 | 
					            reason = "gui tests cannot run without OS X window manager"
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            class ProcessSerialNumber(Structure):
 | 
				
			||||||
 | 
					                _fields_ = [("highLongOfPSN", c_int),
 | 
				
			||||||
 | 
					                            ("lowLongOfPSN", c_int)]
 | 
				
			||||||
 | 
					            psn = ProcessSerialNumber()
 | 
				
			||||||
 | 
					            psn_p = pointer(psn)
 | 
				
			||||||
 | 
					            if (  (app_services.GetCurrentProcess(psn_p) < 0) or
 | 
				
			||||||
 | 
					                  (app_services.SetFrontProcess(psn_p) < 0) ):
 | 
				
			||||||
 | 
					                reason = "cannot run without OS X gui process"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # check on every platform whether tkinter can actually do anything
 | 
				
			||||||
 | 
					    if not reason:
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            from tkinter import Tk
 | 
				
			||||||
 | 
					            root = Tk()
 | 
				
			||||||
 | 
					            root.destroy()
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            err_string = str(e)
 | 
				
			||||||
 | 
					            if len(err_string) > 50:
 | 
				
			||||||
 | 
					                err_string = err_string[:50] + ' [...]'
 | 
				
			||||||
 | 
					            reason = 'Tk unavailable due to {}: {}'.format(type(e).__name__,
 | 
				
			||||||
 | 
					                                                           err_string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _is_gui_available.reason = reason
 | 
				
			||||||
 | 
					    _is_gui_available.result = not reason
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return _is_gui_available.result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def is_resource_enabled(resource):
 | 
					def is_resource_enabled(resource):
 | 
				
			||||||
    """Test whether a resource is enabled.  Known resources are set by
 | 
					    """Test whether a resource is enabled.  Known resources are set by
 | 
				
			||||||
| 
						 | 
					@ -421,7 +464,7 @@ def requires(resource, msg=None):
 | 
				
			||||||
    executing.
 | 
					    executing.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    if resource == 'gui' and not _is_gui_available():
 | 
					    if resource == 'gui' and not _is_gui_available():
 | 
				
			||||||
        raise unittest.SkipTest("Cannot use the 'gui' resource")
 | 
					        raise ResourceDenied(_is_gui_available.reason)
 | 
				
			||||||
    # see if the caller's module is __main__ - if so, treat as if
 | 
					    # see if the caller's module is __main__ - if so, treat as if
 | 
				
			||||||
    # the resource was set
 | 
					    # the resource was set
 | 
				
			||||||
    if sys._getframe(1).f_globals.get("__name__") == "__main__":
 | 
					    if sys._getframe(1).f_globals.get("__name__") == "__main__":
 | 
				
			||||||
| 
						 | 
					@ -1589,7 +1632,7 @@ def _id(obj):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def requires_resource(resource):
 | 
					def requires_resource(resource):
 | 
				
			||||||
    if resource == 'gui' and not _is_gui_available():
 | 
					    if resource == 'gui' and not _is_gui_available():
 | 
				
			||||||
        return unittest.skip("resource 'gui' is not available")
 | 
					        return unittest.skip(_is_gui_available.reason)
 | 
				
			||||||
    if is_resource_enabled(resource):
 | 
					    if is_resource_enabled(resource):
 | 
				
			||||||
        return _id
 | 
					        return _id
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,12 @@
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
from test import support
 | 
					from test import support
 | 
				
			||||||
from test.support import import_module, use_resources
 | 
					from test.support import import_module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Skip test if _thread or _tkinter wasn't built or idlelib was deleted.
 | 
					# Skip test if _thread or _tkinter wasn't built or idlelib was deleted.
 | 
				
			||||||
import_module('threading')  # imported by PyShell, imports _thread
 | 
					import_module('threading')  # imported by PyShell, imports _thread
 | 
				
			||||||
tk = import_module('tkinter')  # imports _tkinter
 | 
					tk = import_module('tkinter')  # imports _tkinter
 | 
				
			||||||
idletest = import_module('idlelib.idle_test')
 | 
					idletest = import_module('idlelib.idle_test')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# If buildbot improperly sets gui resource (#18365, #18441), remove it
 | 
					 | 
				
			||||||
# so requires('gui') tests are skipped while non-gui tests still run.
 | 
					 | 
				
			||||||
# If there is a problem with Macs, see #18441, msg 193805
 | 
					 | 
				
			||||||
if use_resources and 'gui' in use_resources:
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        root = tk.Tk()
 | 
					 | 
				
			||||||
        root.destroy()
 | 
					 | 
				
			||||||
        del root
 | 
					 | 
				
			||||||
    except tk.TclError:
 | 
					 | 
				
			||||||
        while 'gui' in use_resources:
 | 
					 | 
				
			||||||
            use_resources.remove('gui')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Without test_main present, regrtest.runtest_inner (line1219) calls
 | 
					# Without test_main present, regrtest.runtest_inner (line1219) calls
 | 
				
			||||||
# unittest.TestLoader().loadTestsFromModule(this_module) which calls
 | 
					# unittest.TestLoader().loadTestsFromModule(this_module) which calls
 | 
				
			||||||
# load_tests() if it finds it. (Unittest.main does the same.)
 | 
					# load_tests() if it finds it. (Unittest.main does the same.)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,8 +6,7 @@
 | 
				
			||||||
support.import_fresh_module('tkinter')
 | 
					support.import_fresh_module('tkinter')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Skip test if tk cannot be initialized.
 | 
					# Skip test if tk cannot be initialized.
 | 
				
			||||||
from tkinter.test.support import check_tk_availability
 | 
					support.requires('gui')
 | 
				
			||||||
check_tk_availability()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tkinter.test import runtktests
 | 
					from tkinter.test import runtktests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,8 +9,7 @@
 | 
				
			||||||
support.import_fresh_module('tkinter')
 | 
					support.import_fresh_module('tkinter')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Skip test if tk cannot be initialized.
 | 
					# Skip test if tk cannot be initialized.
 | 
				
			||||||
from tkinter.test.support import check_tk_availability
 | 
					support.requires('gui')
 | 
				
			||||||
check_tk_availability()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from _tkinter import TclError
 | 
					from _tkinter import TclError
 | 
				
			||||||
from tkinter import ttk
 | 
					from tkinter import ttk
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,52 +1,10 @@
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import tkinter
 | 
					import tkinter
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					from test.support import requires
 | 
				
			||||||
_tk_unavailable = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def check_tk_availability():
 | 
					 | 
				
			||||||
    """Check that Tk is installed and available."""
 | 
					 | 
				
			||||||
    global _tk_unavailable
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if _tk_unavailable is None:
 | 
					 | 
				
			||||||
        _tk_unavailable = False
 | 
					 | 
				
			||||||
        if sys.platform == 'darwin':
 | 
					 | 
				
			||||||
            # The Aqua Tk implementations on OS X can abort the process if
 | 
					 | 
				
			||||||
            # being called in an environment where a window server connection
 | 
					 | 
				
			||||||
            # cannot be made, for instance when invoked by a buildbot or ssh
 | 
					 | 
				
			||||||
            # process not running under the same user id as the current console
 | 
					 | 
				
			||||||
            # user.  To avoid that, raise an exception if the window manager
 | 
					 | 
				
			||||||
            # connection is not available.
 | 
					 | 
				
			||||||
            from ctypes import cdll, c_int, pointer, Structure
 | 
					 | 
				
			||||||
            from ctypes.util import find_library
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            app_services = cdll.LoadLibrary(find_library("ApplicationServices"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if app_services.CGMainDisplayID() == 0:
 | 
					 | 
				
			||||||
                _tk_unavailable = "cannot run without OS X window manager"
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                class ProcessSerialNumber(Structure):
 | 
					 | 
				
			||||||
                    _fields_ = [("highLongOfPSN", c_int),
 | 
					 | 
				
			||||||
                                ("lowLongOfPSN", c_int)]
 | 
					 | 
				
			||||||
                psn = ProcessSerialNumber()
 | 
					 | 
				
			||||||
                psn_p = pointer(psn)
 | 
					 | 
				
			||||||
                if (  (app_services.GetCurrentProcess(psn_p) < 0) or
 | 
					 | 
				
			||||||
                      (app_services.SetFrontProcess(psn_p) < 0) ):
 | 
					 | 
				
			||||||
                    _tk_unavailable = "cannot run without OS X gui process"
 | 
					 | 
				
			||||||
        else:   # not OS X
 | 
					 | 
				
			||||||
            import tkinter
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                tkinter.Button()
 | 
					 | 
				
			||||||
            except tkinter.TclError as msg:
 | 
					 | 
				
			||||||
                # assuming tk is not available
 | 
					 | 
				
			||||||
                _tk_unavailable = "tk not available: %s" % msg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if _tk_unavailable:
 | 
					 | 
				
			||||||
        raise unittest.SkipTest(_tk_unavailable)
 | 
					 | 
				
			||||||
    return
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_tk_root():
 | 
					def get_tk_root():
 | 
				
			||||||
    check_tk_availability()     # raise exception if tk unavailable
 | 
					    requires('gui')             # raise exception if tk unavailable
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        root = tkinter._default_root
 | 
					        root = tkinter._default_root
 | 
				
			||||||
    except AttributeError:
 | 
					    except AttributeError:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -259,6 +259,10 @@ Documentation
 | 
				
			||||||
Tests
 | 
					Tests
 | 
				
			||||||
-----
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Issue #18604: Consolidated checks for GUI availability.  All platforms now
 | 
				
			||||||
 | 
					  at least check whether Tk can be instantiated when the GUI resource is
 | 
				
			||||||
 | 
					  requested.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Issue #21275: Fix a socket test on KFreeBSD.
 | 
					- Issue #21275: Fix a socket test on KFreeBSD.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Issue #21223: Pass test_site/test_startup_imports when some of the extensions
 | 
					- Issue #21223: Pass test_site/test_startup_imports when some of the extensions
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue