1 # Prelude to allow running this as a main program
5 macresource
.need('DITL', 468, "PythonIDE.rsrc")
6 widgetrespathsegs
= [sys
.exec_prefix
, "Mac", "Tools", "IDE", "Widgets.rsrc"]
7 widgetresfile
= os
.path
.join(*widgetrespathsegs
)
8 if not os
.path
.exists(widgetresfile
):
9 widgetrespathsegs
= [os
.pardir
, "Tools", "IDE", "Widgets.rsrc"]
10 widgetresfile
= os
.path
.join(*widgetrespathsegs
)
11 refno
= macresource
.need('CURS', 468, widgetresfile
)
12 if os
.environ
.has_key('PYTHONIDEPATH'):
13 # For development set this environment variable
14 ide_path
= os
.environ
['PYTHONIDEPATH']
16 # We're not a fullblown application
17 idepathsegs
= [sys
.exec_prefix
, "Mac", "Tools", "IDE"]
18 ide_path
= os
.path
.join(*idepathsegs
)
19 if not os
.path
.exists(ide_path
):
20 idepathsegs
= [os
.pardir
, "Tools", "IDE"]
22 ide_path
= os
.path
.join(*([p
]+idepathsegs
))
23 if os
.path
.exists(ide_path
):
27 # We are a fully frozen application
28 ide_path
= sys
.argv
[0]
29 if ide_path
not in sys
.path
:
30 sys
.path
.insert(0, ide_path
)
32 if __name__
== '__main__':
37 from Carbon
import Evt
48 PACKMAN_HOMEPAGE
="http://www.python.org/packman"
52 USER_INSTALL_DIR
= os
.path
.join(os
.environ
.get('HOME', ''),
58 class PackageManagerMain(Wapplication
.Application
):
61 self
.preffilepath
= os
.path
.join("Python", "Package Install Manager Prefs")
62 Wapplication
.Application
.__init
__(self
, 'Pimp')
64 from Carbon
import AppleEvents
67 AE
.AEInstallEventHandler(AppleEvents
.kCoreEventClass
, AppleEvents
.kAEOpenApplication
,
69 AE
.AEInstallEventHandler(AppleEvents
.kCoreEventClass
, AppleEvents
.kAEReopenApplication
,
71 AE
.AEInstallEventHandler(AppleEvents
.kCoreEventClass
, AppleEvents
.kAEPrintDocuments
,
73 AE
.AEInstallEventHandler(AppleEvents
.kCoreEventClass
, AppleEvents
.kAEQuitApplication
,
77 # With -D option (OSX command line only) keep stderr, for debugging the IDE
80 if len(sys
.argv
) >= 2 and sys
.argv
[1] == '-D':
81 debug_stderr
= sys
.stderr
83 PyConsole
.installoutput()
85 sys
.stderr
= debug_stderr
86 self
.domenu_openstandard()
89 def makeusermenus(self
):
90 m
= Wapplication
.Menu(self
.menubar
, "File")
91 newitem
= FrameWork
.MenuItem(m
, "Open Standard Database", "N", 'openstandard')
92 newexpitem
= FrameWork
.MenuItem(m
, "Open Experimental Database", None, 'openexperimental')
93 newexpitem
.enable(pimp
.PIMP_VERSION
>= "0.4")
94 openitem
= FrameWork
.MenuItem(m
, "Open"+ELIPSES
, "O", 'open')
95 openURLitem
= FrameWork
.MenuItem(m
, "Open URL"+ELIPSES
, "D", 'openURL')
96 FrameWork
.Separator(m
)
97 moreinfoitem
= FrameWork
.MenuItem(m
, "More Databases", None, 'opendatabasepage')
98 FrameWork
.Separator(m
)
99 closeitem
= FrameWork
.MenuItem(m
, "Close", "W", 'close')
100 ## saveitem = FrameWork.MenuItem(m, "Save", "S", 'save')
101 ## saveasitem = FrameWork.MenuItem(m, "Save as"+ELIPSES, None, 'save_as')
102 ## FrameWork.Separator(m)
104 m
= Wapplication
.Menu(self
.menubar
, "Edit")
105 undoitem
= FrameWork
.MenuItem(m
, "Undo", 'Z', "undo")
106 FrameWork
.Separator(m
)
107 cutitem
= FrameWork
.MenuItem(m
, "Cut", 'X', "cut")
108 copyitem
= FrameWork
.MenuItem(m
, "Copy", "C", "copy")
109 pasteitem
= FrameWork
.MenuItem(m
, "Paste", "V", "paste")
110 FrameWork
.MenuItem(m
, "Clear", None, "clear")
111 FrameWork
.Separator(m
)
112 selallitem
= FrameWork
.MenuItem(m
, "Select all", "A", "selectall")
114 m
= Wapplication
.Menu(self
.menubar
, "Package")
115 runitem
= FrameWork
.MenuItem(m
, "Install", "I", 'install')
116 homepageitem
= FrameWork
.MenuItem(m
, "Visit Homepage", None, 'homepage')
118 self
.openwindowsmenu
= Wapplication
.Menu(self
.menubar
, 'Windows')
119 self
.makeopenwindowsmenu()
121 self
._menustocheck
= [closeitem
,
122 undoitem
, cutitem
, copyitem
, pasteitem
,
124 runitem
, homepageitem
]
126 def makehelpmenu(self
):
127 python_app
= os
.path
.join(sys
.prefix
, 'Resources/Python.app')
128 help_source
= os
.path
.join(python_app
, 'Contents/Resources/English.lproj/Documentation')
129 hashelp
= os
.path
.isdir(help_source
)
131 self
.helpmenu
= m
= self
.gethelpmenu()
132 helpitem1
= FrameWork
.MenuItem(m
, "PackageManager Help", None, self
.domenu_packmanhelp
)
133 helpitem1
.enable(hashelp
)
134 helpitem2
= FrameWork
.MenuItem(m
, "MacPython Help", None, self
.domenu_pythonhelp
)
135 helpitem2
.enable(hashelp
)
137 def quitevent(self
, theAppleEvent
, theReply
):
140 def ignoreevent(self
, theAppleEvent
, theReply
):
143 def opendocsevent(self
, theAppleEvent
, theReply
):
146 parameters
, args
= aetools
.unpackevent(theAppleEvent
)
147 docs
= parameters
['----']
148 if type(docs
) <> type([]):
151 fsr
, a
= doc
.FSResolveAlias(None)
152 path
= fsr
.as_pathname()
153 path
= urllib
.pathname2url(path
)
156 def opendoc(self
, url
):
158 self
.defaulturl
= url
161 def getabouttext(self
):
162 return "About Package Manager"+ELIPSES
164 def do_about(self
, id, item
, window
, event
):
165 EasyDialogs
.Message("Package Install Manager for Python\nPackMan engine (pimp) version: %s" %
168 def domenu_openstandard(self
, *args
):
169 if pimp
.PIMP_VERSION
>= "0.4":
170 url
= pimp
.getDefaultDatabase()
176 def domenu_openexperimental(self
, *args
):
177 database
= pimp
.getDefaultDatabase(experimental
=True)
178 self
.opendoc(database
)
180 def domenu_open(self
, *args
):
181 filename
= EasyDialogs
.AskFileForOpen(typeList
=("TEXT",))
183 filename
= urllib
.pathname2url(filename
)
184 if filename
[:5] != 'file:':
185 filename
= 'file:' + filename
186 self
.opendoc(filename
)
188 def domenu_openURL(self
, *args
):
189 ok
= EasyDialogs
.AskYesNoCancel(
190 "Warning: by opening a non-standard database "
191 "you are trusting the maintainer of it "
192 "to run arbitrary code on your machine.",
195 url
= EasyDialogs
.AskString("URL of database to open:",
196 default
=self
.defaulturl
, ok
="Open")
200 def domenu_opendatabasepage(self
):
204 icr
.launchurl(PACKMAN_HOMEPAGE
)
205 def makeopenwindowsmenu(self
):
206 for i
in range(len(self
.openwindowsmenu
.items
)):
207 self
.openwindowsmenu
.menu
.DeleteMenuItem(1)
208 self
.openwindowsmenu
.items
= []
210 self
._openwindows
= {}
211 for window
in self
._windows
.keys():
212 title
= window
.GetWTitle()
215 windows
.append((title
, window
))
217 for title
, window
in windows
:
219 item
= FrameWork
.MenuItem(self
.openwindowsmenu
, title
, shortcut
, callback
= self
.domenu_openwindows
)
220 self
._openwindows
[item
.item
] = window
221 self
._openwindowscheckmark
= 0
222 self
.checkopenwindowsmenu()
224 def domenu_openwindows(self
, id, item
, window
, event
):
225 w
= self
._openwindows
[item
]
229 def domenu_quit(self
):
232 def domenu_save(self
, *args
):
235 def domenu_pythonhelp(self
, *args
):
236 from Carbon
import AH
237 AH
.AHGotoPage("MacPython Help", None, None)
239 def domenu_packmanhelp(self
, *args
):
240 from Carbon
import AH
241 AH
.AHGotoPage("MacPython Help", "packman.html", None)
244 ## import PyConsole, PyEdit
245 for window
in self
._windows
.values():
247 rv
= window
.close() # ignore any errors while quitting
249 rv
= 0 # (otherwise, we can get stuck!)
253 ## PyConsole.console.writeprefs()
254 ## PyConsole.output.writeprefs()
255 ## PyEdit.searchengine.writeprefs()
257 ## # Write to __stderr__ so the msg end up in Console.app and has
258 ## # at least _some_ chance of getting read...
259 ## # But: this is a workaround for way more serious problems with
260 ## # the Python 2.2 Jaguar addon.
261 ## sys.__stderr__.write("*** PythonIDE: Can't write preferences ***\n")
266 def setuppimp(self
, url
):
267 self
.pimpprefs
= pimp
.PimpPreferences()
268 self
.pimpdb
= pimp
.PimpDatabase(self
.pimpprefs
)
270 url
= self
.pimpprefs
.pimpDatabase
272 self
.pimpdb
.appendURL(url
)
274 rv
= "Cannot open %s: %s\n" % (url
, arg
)
275 rv
+= "\nSee MacPython Package Manager help page."
278 rv
= "Unspecified error while parsing database: %s\n" % url
279 rv
+= "Usually, this means the database is not correctly formatted.\n"
280 rv
+= "\nSee MacPython Package Manager help page."
282 # Check whether we can write the installation directory.
283 # If not, set to the per-user directory, possibly
284 # creating it, if needed.
285 installDir
= self
.pimpprefs
.installDir
286 if not os
.access(installDir
, os
.R_OK|os
.W_OK|os
.X_OK
):
287 rv
= self
.setuserinstall(1)
289 return self
.pimpprefs
.check()
293 self
.pimpprefs
= None
297 def setuserinstall(self
, onoff
):
300 if not os
.path
.exists(USER_INSTALL_DIR
):
302 os
.makedirs(USER_INSTALL_DIR
)
305 if not USER_INSTALL_DIR
in sys
.path
:
308 self
.pimpprefs
.setInstallDir(USER_INSTALL_DIR
)
310 self
.pimpprefs
.setInstallDir(None)
311 rv
= rv
+ self
.pimpprefs
.check()
314 def getuserinstall(self
):
315 return self
.pimpprefs
.installDir
== USER_INSTALL_DIR
317 def getbrowserdata(self
, show_hidden
=1):
318 packages
= self
.pimpdb
.list()
320 self
.packages
= packages
324 name
= pkg
.fullname()
325 if name
[0] == '(' and name
[-1] == ')' and not show_hidden
:
327 self
.packages
.append(pkg
)
329 for pkg
in self
.packages
:
330 name
= pkg
.fullname()
331 status
, _
= pkg
.installed()
332 description
= pkg
.description()
333 description_line1
= description
.split('\n')[0]
334 rv
.append((status
, name
, description_line1
))
337 def getstatus(self
, number
):
338 pkg
= self
.packages
[number
]
339 return pkg
.installed()
341 def installpackage(self
, sel
, output
, recursive
, force
):
342 pkg
= self
.packages
[sel
]
343 pimpinstaller
= pimp
.PimpInstaller(self
.pimpdb
)
344 list, messages
= pimpinstaller
.prepareInstall(pkg
, force
, recursive
)
347 messages
= pimpinstaller
.install(list, output
)
350 class PackageBrowser(PimpInterface
):
352 def __init__(self
, url
= None):
354 messages
= self
.setuppimp(url
)
357 self
.showmessages(messages
)
362 def setupwidgets(self
):
363 DESCRIPTION_HEIGHT
= 140
365 STATUS_POS
= INSTALL_POS
- (70 + DESCRIPTION_HEIGHT
)
366 self
.w
= W
.Window((580, 600), "Python Install Manager", minsize
= (400, 400), tabbable
= 0)
367 self
.w
.titlebar
= W
.TextBox((4, 8, 60, 18), 'Packages:')
368 self
.w
.hidden_button
= W
.CheckBox((-100, 4, 0, 18), 'Show Hidden', self
.updatestatus
)
369 data
= self
.getbrowserdata()
370 self
.w
.packagebrowser
= W
.MultiList((4, 24, 0, STATUS_POS
-2), data
, self
.listhit
, cols
=3)
372 self
.w
.installed_l
= W
.TextBox((4, STATUS_POS
, 70, 12), 'Installed:')
373 self
.w
.installed
= W
.TextBox((74, STATUS_POS
, 0, 12), '')
374 self
.w
.message_l
= W
.TextBox((4, STATUS_POS
+20, 70, 12), 'Status:')
375 self
.w
.message
= W
.TextBox((74, STATUS_POS
+20, 0, 12), '')
376 self
.w
.homepage_button
= W
.Button((4, STATUS_POS
+40, 96, 18), 'View homepage', self
.do_homepage
)
377 self
.w
.description_l
= W
.TextBox((4, STATUS_POS
+70, 70, 12), 'Description:')
378 self
.w
.description
= W
.EditText((74, STATUS_POS
+70, 0, DESCRIPTION_HEIGHT
-4))
380 self
.w
.divline
= W
.HorizontalLine((0, INSTALL_POS
-4, 0, 0))
381 self
.w
.verbose_button
= W
.CheckBox((84, INSTALL_POS
+4, 60, 18), 'Verbose')
382 self
.w
.recursive_button
= W
.CheckBox((146, INSTALL_POS
+4, 120, 18), 'Install dependencies', self
.updatestatus
)
383 self
.w
.recursive_button
.set(1)
384 self
.w
.force_button
= W
.CheckBox((268, INSTALL_POS
+4, 70, 18), 'Overwrite', self
.updatestatus
)
385 self
.w
.user_button
= W
.CheckBox((340, INSTALL_POS
+4, 140, 18), 'For Current User Only', self
.do_user
)
386 self
.w
.install_button
= W
.Button((4, INSTALL_POS
+4, 56, 18), 'Install:', self
.do_install
)
388 self
.w
.description
.enable(0)
390 def updatestatus(self
):
391 topcell
= self
.w
.packagebrowser
.gettopcell()
392 sel
= self
.w
.packagebrowser
.getselection()
393 data
= self
.getbrowserdata(self
.w
.hidden_button
.get())
394 self
.w
.packagebrowser
.setitems(data
)
395 self
.w
.user_button
.set(self
.getuserinstall())
397 self
.w
.installed
.set('')
398 self
.w
.message
.set('')
399 self
.w
.install_button
.enable(0)
400 self
.w
.homepage_button
.enable(0)
401 self
.w
.description
.set('')
402 self
.w
.verbose_button
.enable(0)
403 self
.w
.recursive_button
.enable(0)
404 self
.w
.force_button
.enable(0)
405 self
.w
.user_button
.enable(0)
408 if sel
>= len(self
.packages
):
410 self
.w
.packagebrowser
.setselection([sel
])
411 installed
, message
= self
.getstatus(sel
)
412 self
.w
.installed
.set(installed
)
413 self
.w
.message
.set(message
)
414 self
.w
.install_button
.enable(installed
!= "yes" or self
.w
.force_button
.get())
415 self
.w
.homepage_button
.enable(not not self
.packages
[sel
].homepage())
416 description
= self
.packages
[sel
].description()
417 description
= description
.splitlines()
418 description
= '\r'.join(description
)
419 self
.w
.description
.set(description
)
420 self
.w
.verbose_button
.enable(1)
421 self
.w
.recursive_button
.enable(1)
422 self
.w
.force_button
.enable(1)
423 self
.w
.user_button
.enable(1)
424 self
.w
.packagebrowser
.settopcell(topcell
)
426 def listhit(self
, *args
, **kwargs
):
429 def do_install(self
):
430 sel
= self
.w
.packagebrowser
.getselection()[0]
431 if self
.w
.verbose_button
.get():
435 recursive
= self
.w
.recursive_button
.get()
436 force
= self
.w
.force_button
.get()
437 messages
= self
.installpackage(sel
, output
, recursive
, force
)
444 self
.showmessages(messages
)
446 def showmessages(self
, messages
):
448 # To be on the safe side we always show the hidden packages,
449 # they may be referred to in the error messages.
450 if not self
.w
.hidden_button
.get():
451 self
.w
.hidden_button
.set(1)
453 if type(messages
) == list:
454 messages
= '\n'.join(messages
)
455 if self
.w
.verbose_button
.get():
456 sys
.stdout
.write(messages
+ '\n')
457 EasyDialogs
.Message(messages
)
459 def do_homepage(self
):
460 sel
= self
.w
.packagebrowser
.getselection()[0]
465 self
.ic
.launchurl(self
.packages
[sel
].homepage())
468 messages
= self
.setuserinstall(self
.w
.user_button
.get())
470 self
.showmessages(messages
)
472 if __name__
== '__main__':