diff --git a/Lib/idlelib/Bindings.py b/Lib/idlelib/Bindings.py index 13e2a3346fe..74a93d3b141 100644 --- a/Lib/idlelib/Bindings.py +++ b/Lib/idlelib/Bindings.py @@ -10,6 +10,7 @@ """ import sys from idlelib.configHandler import idleConf +from idlelib import macosxSupport menudefs = [ # underscore prefixes character to underscore @@ -80,7 +81,7 @@ ]), ] -if sys.platform == 'darwin' and '.app' in sys.executable: +if macosxSupport.runningAsOSXApp(): # Running as a proper MacOS application bundle. This block restructures # the menus a little to make them conform better to the HIG. diff --git a/Lib/idlelib/EditorWindow.py b/Lib/idlelib/EditorWindow.py index 1512c464af4..38ae7b60aa4 100644 --- a/Lib/idlelib/EditorWindow.py +++ b/Lib/idlelib/EditorWindow.py @@ -363,7 +363,7 @@ def createmenubar(self): underline, label = prepstr(label) menudict[name] = menu = Menu(mbar, name=name) mbar.add_cascade(label=label, menu=menu, underline=underline) - if sys.platform == 'darwin' and '.framework' in sys.executable: + if macosxSupport.runningAsOSXApp(): # Insert the application menu menudict['application'] = menu = Menu(mbar, name='apple') mbar.add_cascade(label='IDLE', menu=menu) diff --git a/Lib/idlelib/MultiCall.py b/Lib/idlelib/MultiCall.py index 1c40638402a..47f402d3261 100644 --- a/Lib/idlelib/MultiCall.py +++ b/Lib/idlelib/MultiCall.py @@ -32,6 +32,7 @@ import sys import re import tkinter +from idlelib import macosxSupport # the event type constants, which define the meaning of mc_type MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; @@ -44,7 +45,7 @@ MC_OPTION = 1<<6; MC_COMMAND = 1<<7 # define the list of modifiers, to be used in complex event types. -if sys.platform == "darwin" and sys.executable.count(".app"): +if macosxSupport.runningAsOSXApp(): _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",)) _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND) else: diff --git a/Lib/idlelib/keybindingDialog.py b/Lib/idlelib/keybindingDialog.py index f5d32efa636..b99c5e0d8c0 100644 --- a/Lib/idlelib/keybindingDialog.py +++ b/Lib/idlelib/keybindingDialog.py @@ -4,6 +4,7 @@ from tkinter import * import tkinter.messagebox as tkMessageBox import string +from idlelib import macosxSupport class GetKeysDialog(Toplevel): def __init__(self,parent,title,action,currentKeySequences): @@ -133,7 +134,7 @@ def SetModifiersForPlatform(self): config-keys.def must use the same ordering. """ import sys - if sys.platform == 'darwin' and sys.argv[0].count('.app'): + if macosxSupport.runningAsOSXApp(): self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: self.modifiers = ['Control', 'Alt', 'Shift'] diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosxSupport.py index 9f92b6c9dab..e759157444a 100644 --- a/Lib/idlelib/macosxSupport.py +++ b/Lib/idlelib/macosxSupport.py @@ -6,8 +6,12 @@ import tkinter def runningAsOSXApp(): - """ Returns True iff running from the IDLE.app bundle on OSX """ - return (sys.platform == 'darwin' and 'IDLE.app' in sys.argv[0]) + """ + Returns True if Python is running from within an app on OSX. + If so, assume that Python was built with Aqua Tcl/Tk rather than + X11 Tck/Tk. + """ + return (sys.platform == 'darwin' and '.app' in sys.executable) def addOpenEventSupport(root, flist): """ diff --git a/Mac/IDLE/IDLE.app/Contents/MacOS/IDLE b/Mac/IDLE/IDLE.app/Contents/MacOS/IDLE index ecf29bf0875..61b02fff6bd 100755 --- a/Mac/IDLE/IDLE.app/Contents/MacOS/IDLE +++ b/Mac/IDLE/IDLE.app/Contents/MacOS/IDLE @@ -1,4 +1,4 @@ -#!%prefix%/Resources/Python.app/Contents/MacOS/Python3 +#!%prefix%/Resources/Python.app/Contents/MacOS/%exe% import sys, os execdir = os.path.dirname(sys.argv[0]) diff --git a/Mac/IDLE/IDLE.app/Contents/Resources/idlemain.py b/Mac/IDLE/IDLE.app/Contents/Resources/idlemain.py index 9b5273899f6..d6803ba832c 100644 --- a/Mac/IDLE/IDLE.app/Contents/Resources/idlemain.py +++ b/Mac/IDLE/IDLE.app/Contents/Resources/idlemain.py @@ -3,8 +3,6 @@ """ import sys, os -from idlelib.PyShell import main - # Change the current directory the user's home directory, that way we'll get # a more useful default location in the open/save dialogs. os.chdir(os.path.expanduser('~/Documents')) @@ -13,10 +11,54 @@ # Make sure sys.executable points to the python interpreter inside the # framework, instead of at the helper executable inside the application # bundle (the latter works, but doesn't allow access to the window server) -if sys.executable.endswith('-32'): - sys.executable = os.path.join(sys.prefix, 'bin', 'python-32') -else: - sys.executable = os.path.join(sys.prefix, 'bin', 'python') +# +# .../IDLE.app/ +# Contents/ +# MacOS/ +# IDLE (a python script) +# Python{-32} (symlink) +# Resources/ +# idlemain.py (this module) +# ... +# +# ../IDLE.app/Contents/MacOS/Python{-32} is symlinked to +# ..Library/Frameworks/Python.framework/Versions/m.n +# /Resources/Python.app/Contents/MacOS/Python{-32} +# which is the Python interpreter executable +# +# The flow of control is as follows: +# 1. IDLE.app is launched which starts python running the IDLE script +# 2. IDLE script exports +# PYTHONEXECUTABLE = .../IDLE.app/Contents/MacOS/Python{-32} +# (the symlink to the framework python) +# 3. IDLE script alters sys.argv and uses os.execve to replace itself with +# idlemain.py running under the symlinked python. +# This is the magic step. +# 4. During interpreter initialization, because PYTHONEXECUTABLE is defined, +# sys.executable may get set to an unuseful value. +# +# (Note that the IDLE script and the setting of PYTHONEXECUTABLE is +# generated automatically by bundlebuilder in the Python 2.x build. +# Also, IDLE invoked via command line, i.e. bin/idle, bypasses all of +# this.) +# +# Now fix up the execution environment before importing idlelib. + +# Reset sys.executable to its normal value, the actual path of +# the interpreter in the framework, by following the symlink +# exported in PYTHONEXECUTABLE. +pyex = os.environ['PYTHONEXECUTABLE'] +sys.executable = os.path.join(os.path.dirname(pyex), os.readlink(pyex)) + +# Remove any sys.path entries for the Resources dir in the IDLE.app bundle. +p = pyex.partition('.app') +if p[2].startswith('/Contents/MacOS/Python'): + sys.path = [value for value in sys.path if + value.partition('.app') != (p[0], p[1], '/Contents/Resources')] + +# Unexport PYTHONEXECUTABLE so that the other Python processes started +# by IDLE have a normal sys.executable. +del os.environ['PYTHONEXECUTABLE'] # Look for the -psn argument that the launcher adds and remove it, it will # only confuse the IDLE startup code. @@ -25,6 +67,7 @@ del sys.argv[idx] break -#argvemulator.ArgvCollector().mainloop() +# Now it is safe to import idlelib. +from idlelib.PyShell import main if __name__ == '__main__': main() diff --git a/Mac/Makefile.in b/Mac/Makefile.in index f28d1ea1936..e847ba90882 100644 --- a/Mac/Makefile.in +++ b/Mac/Makefile.in @@ -215,7 +215,7 @@ install_Python4way: install_Python install_IDLE: test -d "$(DESTDIR)$(PYTHONAPPSDIR)" || mkdir -p "$(DESTDIR)$(PYTHONAPPSDIR)" - -test -d "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app" && rm -r "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app" + -test -d "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app" && rm -rf "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app" /bin/cp -PR "$(srcdir)/IDLE/IDLE.app" "$(DESTDIR)$(PYTHONAPPSDIR)" ln -sf $(INSTALLED_PYTHONAPP) "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app/Contents/MacOS/Python" sed -e "s!%prefix%!$(prefix)!g" -e 's!%exe%!$(PYTHONFRAMEWORK)!g' < "$(srcdir)/IDLE/IDLE.app/Contents/MacOS/IDLE" > "$(DESTDIR)$(PYTHONAPPSDIR)/IDLE.app/Contents/MacOS/IDLE"