mirror of
https://github.com/python/cpython.git
synced 2025-11-06 08:31:58 +00:00
cause an index error. We now select the first package if this threatens to happen. Will backport.
451 lines
14 KiB
Python
Executable file
451 lines
14 KiB
Python
Executable file
# Prelude to allow running this as a main program
|
|
def _init():
|
|
import macresource
|
|
import sys, os
|
|
macresource.need('DITL', 468, "PythonIDE.rsrc")
|
|
widgetrespathsegs = [sys.exec_prefix, "Mac", "Tools", "IDE", "Widgets.rsrc"]
|
|
widgetresfile = os.path.join(*widgetrespathsegs)
|
|
if not os.path.exists(widgetresfile):
|
|
widgetrespathsegs = [os.pardir, "Tools", "IDE", "Widgets.rsrc"]
|
|
widgetresfile = os.path.join(*widgetrespathsegs)
|
|
refno = macresource.need('CURS', 468, widgetresfile)
|
|
if os.environ.has_key('PYTHONIDEPATH'):
|
|
# For development set this environment variable
|
|
ide_path = os.environ['PYTHONIDEPATH']
|
|
elif refno:
|
|
# We're not a fullblown application
|
|
idepathsegs = [sys.exec_prefix, "Mac", "Tools", "IDE"]
|
|
ide_path = os.path.join(*idepathsegs)
|
|
if not os.path.exists(ide_path):
|
|
idepathsegs = [os.pardir, "Tools", "IDE"]
|
|
for p in sys.path:
|
|
ide_path = os.path.join(*([p]+idepathsegs))
|
|
if os.path.exists(ide_path):
|
|
break
|
|
|
|
else:
|
|
# We are a fully frozen application
|
|
ide_path = sys.argv[0]
|
|
if ide_path not in sys.path:
|
|
sys.path.insert(0, ide_path)
|
|
|
|
if __name__ == '__main__':
|
|
_init()
|
|
|
|
import W
|
|
import Wapplication
|
|
from Carbon import Evt
|
|
import EasyDialogs
|
|
import FrameWork
|
|
|
|
import sys
|
|
import string
|
|
import os
|
|
import urllib
|
|
|
|
import pimp
|
|
|
|
ELIPSES = '...'
|
|
|
|
USER_INSTALL_DIR = os.path.join(os.environ.get('HOME', ''),
|
|
'Library',
|
|
'Python',
|
|
sys.version[:3],
|
|
'site-packages')
|
|
|
|
class PackageManagerMain(Wapplication.Application):
|
|
|
|
def __init__(self):
|
|
self.preffilepath = os.path.join("Python", "Package Install Manager Prefs")
|
|
Wapplication.Application.__init__(self, 'Pimp')
|
|
from Carbon import AE
|
|
from Carbon import AppleEvents
|
|
|
|
AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEOpenApplication,
|
|
self.ignoreevent)
|
|
AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEReopenApplication,
|
|
self.ignoreevent)
|
|
AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEPrintDocuments,
|
|
self.ignoreevent)
|
|
AE.AEInstallEventHandler(AppleEvents.kCoreEventClass, AppleEvents.kAEQuitApplication,
|
|
self.quitevent)
|
|
if 1:
|
|
import PyConsole
|
|
# With -D option (OSX command line only) keep stderr, for debugging the IDE
|
|
# itself.
|
|
debug_stderr = None
|
|
if len(sys.argv) >= 2 and sys.argv[1] == '-D':
|
|
debug_stderr = sys.stderr
|
|
del sys.argv[1]
|
|
PyConsole.installoutput()
|
|
if debug_stderr:
|
|
sys.stderr = debug_stderr
|
|
self.opendoc(None)
|
|
self.mainloop()
|
|
|
|
def makeusermenus(self):
|
|
m = Wapplication.Menu(self.menubar, "File")
|
|
newitem = FrameWork.MenuItem(m, "Open Standard Database", "N", 'openstandard')
|
|
openitem = FrameWork.MenuItem(m, "Open"+ELIPSES, "O", 'open')
|
|
openURLitem = FrameWork.MenuItem(m, "Open URL"+ELIPSES, "D", 'openURL')
|
|
FrameWork.Separator(m)
|
|
closeitem = FrameWork.MenuItem(m, "Close", "W", 'close')
|
|
## saveitem = FrameWork.MenuItem(m, "Save", "S", 'save')
|
|
## saveasitem = FrameWork.MenuItem(m, "Save as"+ELIPSES, None, 'save_as')
|
|
## FrameWork.Separator(m)
|
|
|
|
m = Wapplication.Menu(self.menubar, "Edit")
|
|
undoitem = FrameWork.MenuItem(m, "Undo", 'Z', "undo")
|
|
FrameWork.Separator(m)
|
|
cutitem = FrameWork.MenuItem(m, "Cut", 'X', "cut")
|
|
copyitem = FrameWork.MenuItem(m, "Copy", "C", "copy")
|
|
pasteitem = FrameWork.MenuItem(m, "Paste", "V", "paste")
|
|
FrameWork.MenuItem(m, "Clear", None, "clear")
|
|
FrameWork.Separator(m)
|
|
selallitem = FrameWork.MenuItem(m, "Select all", "A", "selectall")
|
|
|
|
m = Wapplication.Menu(self.menubar, "Package")
|
|
runitem = FrameWork.MenuItem(m, "Install", "I", 'install')
|
|
homepageitem = FrameWork.MenuItem(m, "Visit Homepage", None, 'homepage')
|
|
|
|
self.openwindowsmenu = Wapplication.Menu(self.menubar, 'Windows')
|
|
self.makeopenwindowsmenu()
|
|
self.makehelpmenu()
|
|
self._menustocheck = [closeitem,
|
|
undoitem, cutitem, copyitem, pasteitem,
|
|
selallitem,
|
|
runitem, homepageitem]
|
|
|
|
def makehelpmenu(self):
|
|
python_app = os.path.join(sys.prefix, 'Resources/Python.app')
|
|
help_source = os.path.join(python_app, 'Contents/Resources/English.lproj/Documentation')
|
|
hashelp = os.path.isdir(help_source)
|
|
|
|
self.helpmenu = m = self.gethelpmenu()
|
|
helpitem1 = FrameWork.MenuItem(m, "PackageManager Help", None, self.domenu_packmanhelp)
|
|
helpitem1.enable(hashelp)
|
|
helpitem2 = FrameWork.MenuItem(m, "MacPython Help", None, self.domenu_pythonhelp)
|
|
helpitem2.enable(hashelp)
|
|
|
|
def quitevent(self, theAppleEvent, theReply):
|
|
self._quit()
|
|
|
|
def ignoreevent(self, theAppleEvent, theReply):
|
|
pass
|
|
|
|
def opendocsevent(self, theAppleEvent, theReply):
|
|
W.SetCursor('watch')
|
|
import aetools
|
|
parameters, args = aetools.unpackevent(theAppleEvent)
|
|
docs = parameters['----']
|
|
if type(docs) <> type([]):
|
|
docs = [docs]
|
|
for doc in docs:
|
|
fsr, a = doc.FSResolveAlias(None)
|
|
path = fsr.as_pathname()
|
|
path = urllib.pathname2url(path)
|
|
self.opendoc(path)
|
|
|
|
def opendoc(self, url):
|
|
PackageBrowser(url)
|
|
|
|
def getabouttext(self):
|
|
return "About Package Manager"+ELIPSES
|
|
|
|
def do_about(self, id, item, window, event):
|
|
EasyDialogs.Message("Package Install Manager for Python")
|
|
|
|
def domenu_openstandard(self, *args):
|
|
self.opendoc(None)
|
|
|
|
def domenu_open(self, *args):
|
|
filename = EasyDialogs.AskFileForOpen(typeList=("TEXT",))
|
|
if filename:
|
|
filename = urllib.pathname2url(filename)
|
|
self.opendoc(filename)
|
|
|
|
def domenu_openURL(self, *args):
|
|
ok = EasyDialogs.AskYesNoCancel(
|
|
"Warning: by opening a non-standard database "
|
|
"you are trusting the maintainer of it "
|
|
"to run arbitrary code on your machine.",
|
|
yes="OK", no="")
|
|
if ok <= 0: return
|
|
url = EasyDialogs.AskString("URL of database to open:", ok="Open")
|
|
if url:
|
|
self.opendoc(url)
|
|
|
|
def domenu_openbyname(self, *args):
|
|
url = EasyDialogs.AskString("Open URL:", ok="Open")
|
|
if url:
|
|
self.opendoc(url)
|
|
|
|
def makeopenwindowsmenu(self):
|
|
for i in range(len(self.openwindowsmenu.items)):
|
|
self.openwindowsmenu.menu.DeleteMenuItem(1)
|
|
self.openwindowsmenu.items = []
|
|
windows = []
|
|
self._openwindows = {}
|
|
for window in self._windows.keys():
|
|
title = window.GetWTitle()
|
|
if not title:
|
|
title = "<no title>"
|
|
windows.append((title, window))
|
|
windows.sort()
|
|
for title, window in windows:
|
|
shortcut = None
|
|
item = FrameWork.MenuItem(self.openwindowsmenu, title, shortcut, callback = self.domenu_openwindows)
|
|
self._openwindows[item.item] = window
|
|
self._openwindowscheckmark = 0
|
|
self.checkopenwindowsmenu()
|
|
|
|
def domenu_openwindows(self, id, item, window, event):
|
|
w = self._openwindows[item]
|
|
w.ShowWindow()
|
|
w.SelectWindow()
|
|
|
|
def domenu_quit(self):
|
|
self._quit()
|
|
|
|
def domenu_save(self, *args):
|
|
print "Save"
|
|
|
|
def domenu_pythonhelp(self, *args):
|
|
from Carbon import AH
|
|
AH.AHGotoPage("MacPython Help", None, None)
|
|
|
|
def domenu_packmanhelp(self, *args):
|
|
from Carbon import AH
|
|
AH.AHGotoPage("MacPython Help", "packman.html", None)
|
|
|
|
def _quit(self):
|
|
## import PyConsole, PyEdit
|
|
for window in self._windows.values():
|
|
try:
|
|
rv = window.close() # ignore any errors while quitting
|
|
except:
|
|
rv = 0 # (otherwise, we can get stuck!)
|
|
if rv and rv > 0:
|
|
return
|
|
## try:
|
|
## PyConsole.console.writeprefs()
|
|
## PyConsole.output.writeprefs()
|
|
## PyEdit.searchengine.writeprefs()
|
|
## except:
|
|
## # Write to __stderr__ so the msg end up in Console.app and has
|
|
## # at least _some_ chance of getting read...
|
|
## # But: this is a workaround for way more serious problems with
|
|
## # the Python 2.2 Jaguar addon.
|
|
## sys.__stderr__.write("*** PythonIDE: Can't write preferences ***\n")
|
|
self.quitting = 1
|
|
|
|
class PimpInterface:
|
|
|
|
def setuppimp(self, url):
|
|
self.pimpprefs = pimp.PimpPreferences()
|
|
self.pimpdb = pimp.PimpDatabase(self.pimpprefs)
|
|
if not url:
|
|
url = self.pimpprefs.pimpDatabase
|
|
try:
|
|
self.pimpdb.appendURL(url)
|
|
except IOError, arg:
|
|
rv = "Cannot open %s: %s\n" % (url, arg)
|
|
rv += "\nSee MacPython Package Manager help page."
|
|
return rv
|
|
except:
|
|
rv = "Unspecified error while parsing database: %s\n" % url
|
|
rv += "Usually, this means the database is not correctly formatted.\n"
|
|
rv += "\nSee MacPython Package Manager help page."
|
|
return rv
|
|
# Check whether we can write the installation directory.
|
|
# If not, set to the per-user directory, possibly
|
|
# creating it, if needed.
|
|
installDir = self.pimpprefs.installDir
|
|
if not os.access(installDir, os.R_OK|os.W_OK|os.X_OK):
|
|
rv = self.setuserinstall(1)
|
|
if rv: return rv
|
|
return self.pimpprefs.check()
|
|
|
|
def closepimp(self):
|
|
self.pimpdb.close()
|
|
self.pimpprefs = None
|
|
self.pimpdb = None
|
|
self.packages = []
|
|
|
|
def setuserinstall(self, onoff):
|
|
rv = ""
|
|
if onoff:
|
|
if not os.path.exists(USER_INSTALL_DIR):
|
|
try:
|
|
os.makedirs(USER_INSTALL_DIR)
|
|
except OSError, arg:
|
|
rv = rv + arg + "\n"
|
|
if not USER_INSTALL_DIR in sys.path:
|
|
import site
|
|
reload(site)
|
|
self.pimpprefs.setInstallDir(USER_INSTALL_DIR)
|
|
else:
|
|
self.pimpprefs.setInstallDir(None)
|
|
rv = rv + self.pimpprefs.check()
|
|
return rv
|
|
|
|
def getuserinstall(self):
|
|
return self.pimpprefs.installDir == USER_INSTALL_DIR
|
|
|
|
def getbrowserdata(self, show_hidden=1):
|
|
packages = self.pimpdb.list()
|
|
if show_hidden:
|
|
self.packages = packages
|
|
else:
|
|
self.packages = []
|
|
for pkg in packages:
|
|
name = pkg.fullname()
|
|
if name[0] == '(' and name[-1] == ')' and not show_hidden:
|
|
continue
|
|
self.packages.append(pkg)
|
|
rv = []
|
|
for pkg in self.packages:
|
|
name = pkg.fullname()
|
|
status, _ = pkg.installed()
|
|
description = pkg.description()
|
|
description_line1 = description.split('\n')[0]
|
|
rv.append((status, name, description_line1))
|
|
return rv
|
|
|
|
def getstatus(self, number):
|
|
pkg = self.packages[number]
|
|
return pkg.installed()
|
|
|
|
def installpackage(self, sel, output, recursive, force):
|
|
pkg = self.packages[sel]
|
|
pimpinstaller = pimp.PimpInstaller(self.pimpdb)
|
|
list, messages = pimpinstaller.prepareInstall(pkg, force, recursive)
|
|
if messages:
|
|
return messages
|
|
messages = pimpinstaller.install(list, output)
|
|
return messages
|
|
|
|
class PackageBrowser(PimpInterface):
|
|
|
|
def __init__(self, url = None):
|
|
self.ic = None
|
|
messages = self.setuppimp(url)
|
|
self.setupwidgets()
|
|
self.updatestatus()
|
|
self.showmessages(messages)
|
|
|
|
def close(self):
|
|
self.closepimp()
|
|
|
|
def setupwidgets(self):
|
|
DESCRIPTION_HEIGHT = 140
|
|
INSTALL_POS = -30
|
|
STATUS_POS = INSTALL_POS - (70 + DESCRIPTION_HEIGHT)
|
|
self.w = W.Window((580, 600), "Python Install Manager", minsize = (400, 400), tabbable = 0)
|
|
self.w.titlebar = W.TextBox((4, 8, 60, 18), 'Packages:')
|
|
self.w.hidden_button = W.CheckBox((-100, 4, 0, 18), 'Show Hidden', self.updatestatus)
|
|
data = self.getbrowserdata()
|
|
self.w.packagebrowser = W.MultiList((4, 24, 0, STATUS_POS-2), data, self.listhit, cols=3)
|
|
|
|
self.w.installed_l = W.TextBox((4, STATUS_POS, 70, 12), 'Installed:')
|
|
self.w.installed = W.TextBox((74, STATUS_POS, 0, 12), '')
|
|
self.w.message_l = W.TextBox((4, STATUS_POS+20, 70, 12), 'Status:')
|
|
self.w.message = W.TextBox((74, STATUS_POS+20, 0, 12), '')
|
|
self.w.homepage_button = W.Button((4, STATUS_POS+40, 96, 18), 'View homepage', self.do_homepage)
|
|
self.w.description_l = W.TextBox((4, STATUS_POS+70, 70, 12), 'Description:')
|
|
self.w.description = W.EditText((74, STATUS_POS+70, 0, DESCRIPTION_HEIGHT-4))
|
|
|
|
self.w.divline = W.HorizontalLine((0, INSTALL_POS-4, 0, 0))
|
|
self.w.verbose_button = W.CheckBox((84, INSTALL_POS+4, 60, 18), 'Verbose')
|
|
self.w.recursive_button = W.CheckBox((146, INSTALL_POS+4, 120, 18), 'Install dependencies', self.updatestatus)
|
|
self.w.recursive_button.set(1)
|
|
self.w.force_button = W.CheckBox((268, INSTALL_POS+4, 70, 18), 'Overwrite', self.updatestatus)
|
|
self.w.user_button = W.CheckBox((340, INSTALL_POS+4, 140, 18), 'For Current User Only', self.do_user)
|
|
self.w.install_button = W.Button((4, INSTALL_POS+4, 56, 18), 'Install:', self.do_install)
|
|
self.w.open()
|
|
self.w.description.enable(0)
|
|
|
|
def updatestatus(self):
|
|
topcell = self.w.packagebrowser.gettopcell()
|
|
sel = self.w.packagebrowser.getselection()
|
|
data = self.getbrowserdata(self.w.hidden_button.get())
|
|
self.w.packagebrowser.setitems(data)
|
|
self.w.user_button.set(self.getuserinstall())
|
|
if len(sel) != 1:
|
|
self.w.installed.set('')
|
|
self.w.message.set('')
|
|
self.w.install_button.enable(0)
|
|
self.w.homepage_button.enable(0)
|
|
self.w.description.set('')
|
|
self.w.verbose_button.enable(0)
|
|
self.w.recursive_button.enable(0)
|
|
self.w.force_button.enable(0)
|
|
self.w.user_button.enable(0)
|
|
else:
|
|
sel = sel[0]
|
|
if sel >= len(self.packages):
|
|
sel = 0
|
|
self.w.packagebrowser.setselection([sel])
|
|
installed, message = self.getstatus(sel)
|
|
self.w.installed.set(installed)
|
|
self.w.message.set(message)
|
|
self.w.install_button.enable(installed != "yes" or self.w.force_button.get())
|
|
self.w.homepage_button.enable(not not self.packages[sel].homepage())
|
|
description = self.packages[sel].description()
|
|
description = description.splitlines()
|
|
description = '\r'.join(description)
|
|
self.w.description.set(description)
|
|
self.w.verbose_button.enable(1)
|
|
self.w.recursive_button.enable(1)
|
|
self.w.force_button.enable(1)
|
|
self.w.user_button.enable(1)
|
|
self.w.packagebrowser.settopcell(topcell)
|
|
|
|
def listhit(self, *args, **kwargs):
|
|
self.updatestatus()
|
|
|
|
def do_install(self):
|
|
sel = self.w.packagebrowser.getselection()[0]
|
|
if self.w.verbose_button.get():
|
|
output = sys.stdout
|
|
else:
|
|
output = None
|
|
recursive = self.w.recursive_button.get()
|
|
force = self.w.force_button.get()
|
|
messages = self.installpackage(sel, output, recursive, force)
|
|
|
|
# Re-read .pth files
|
|
import site
|
|
reload(site)
|
|
|
|
self.updatestatus()
|
|
self.showmessages(messages)
|
|
|
|
def showmessages(self, messages):
|
|
if messages:
|
|
# To be on the safe side we always show the hidden packages,
|
|
# they may be referred to in the error messages.
|
|
if not self.w.hidden_button.get():
|
|
self.w.hidden_button.set(1)
|
|
self.updatestatus()
|
|
if type(messages) == list:
|
|
messages = '\n'.join(messages)
|
|
if self.w.verbose_button.get():
|
|
sys.stdout.write(messages + '\n')
|
|
EasyDialogs.Message(messages)
|
|
|
|
def do_homepage(self):
|
|
sel = self.w.packagebrowser.getselection()[0]
|
|
if not self.ic:
|
|
import ic
|
|
|
|
self.ic = ic.IC()
|
|
self.ic.launchurl(self.packages[sel].homepage())
|
|
|
|
def do_user(self):
|
|
messages = self.setuserinstall(self.w.user_button.get())
|
|
self.updatestatus()
|
|
self.showmessages(messages)
|
|
|
|
if __name__ == '__main__':
|
|
PackageManagerMain()
|