Thread: [Pyobjc-dev] NSApplicationMain idiom: wish to standardize
Brought to you by:
ronaldoussoren
From: Just v. R. <ju...@le...> - 2003-03-20 14:23:43
|
I've been using the following idiom to enter the Cocoa main event loop for some time now: def unexpectedErrorAlert(): exceptionInfo = traceback.format_exception_only(*sys.exc_info()[:2])[0].strip() return NSRunAlertPanel("An unexpected error has occurred", "(%s)" % exceptionInfo, "Continue", "Quit", None) mainFunc = NSApplicationMain args = (sys.argv,) while 1: try: mainFunc(*args) except: traceback.print_exc() if not unexpectedErrorAlert(): break mainFunc = NSApp().run args = () else: break It's more complicated than what I originally thought was needed back when we discussed NSApplicationMain raising exceptions, as simply calling NSApplicationMain() again causes the main nib to be reloaded. The thing is, it's quite a few lines now, and I keep copying it to new apps. I would like to put it in a module, but if it's deemed general enough, I could also integrate it into PyObjC somewhere. What do people think, is it desirable to have in PyObjC? I would propose a new submodule op AppKit, eg. named mainHelper.py. The above could then become from AppKit import mainHelper mainHelper.runEventLoop() Better names are gladly accepted... Just |
From: Ronald O. <ous...@ci...> - 2003-03-21 16:44:17
|
On Thursday, Mar 20, 2003, at 15:23 Europe/Amsterdam, Just van Rossum wrote: > I've been using the following idiom to enter the Cocoa main event loop > for > some time now: [example removed] > It's more complicated than what I originally thought was needed back > when we > discussed NSApplicationMain raising exceptions, as simply calling > NSApplicationMain() again causes the main nib to be reloaded. > > The thing is, it's quite a few lines now, and I keep copying it to new > apps. > I would like to put it in a module, but if it's deemed general enough, > I > could also integrate it into PyObjC somewhere. What do people think, > is it > desirable to have in PyObjC? I would propose a new submodule op > AppKit, eg. > named mainHelper.py. The above could then become > > from AppKit import mainHelper > mainHelper.runEventLoop() > > Better names are gladly accepted... I think this would be very usefull. However, I think this module should be in a seperate package to make it clear that this is a PyObjC helper module instead of wrapped Cocoa functionality. Foundation.Conversion should be moved to this package as well. How about PyObjCTools? - module PyObjCTools.Conversion would be the new name of Foundation.Conversion - module PyObjCTools.AppHelper would contain your function and other NSApplication related tools Speaking of NSApplicationMain... Does anyone care to offer on opinion on the enormous amount of time between the call to NSApplicationMain and the call to awakeFromNib in a simple application? That period of time takes about 4 seconds of the 5 seconds my application needs to start up (using a savagely butchered version of PyObjC, 'import AppKit' would use another 2 seconds if I hadn't build a mini module that exports all definitions I need from AppKit and Foundation). Ronald |
From: <bb...@ma...> - 2003-03-21 16:52:36
|
On Friday, Mar 21, 2003, at 11:33 US/Eastern, Ronald Oussoren wrote: > Speaking of NSApplicationMain... Does anyone care to offer on opinion > on the enormous amount of time between the call to NSApplicationMain > and the call to awakeFromNib in a simple application? That period of > time takes about 4 seconds of the 5 seconds my application needs to > start up (using a savagely butchered version of PyObjC, 'import > AppKit' would use another 2 seconds if I hadn't build a mini module > that exports all definitions I need from AppKit and Foundation). How long does it normally take to launch an app of similar complexity on your machine? The period of time between NSApplicationMain() and the first awakeFromNib() is comprised of most of the "hard stuff" of launching an app; setting up the notification stuff, the mach ports, and all the other innards that integrate the App into OS X.... Unless we have a bug somewhere, that period of time shouldn't really be any longer than a 'normal' app as there really isn't anything from PyObjC that is involved (unless your app delegate implements willFinishLaunching())? b.bum |
From: Ronald O. <ous...@ci...> - 2003-03-21 18:04:25
|
On Friday, Mar 21, 2003, at 17:52 Europe/Amsterdam, bb...@ma... wrote: > On Friday, Mar 21, 2003, at 11:33 US/Eastern, Ronald Oussoren wrote: >> Speaking of NSApplicationMain... Does anyone care to offer on opinion >> on the enormous amount of time between the call to NSApplicationMain >> and the call to awakeFromNib in a simple application? That period of >> time takes about 4 seconds of the 5 seconds my application needs to >> start up (using a savagely butchered version of PyObjC, 'import >> AppKit' would use another 2 seconds if I hadn't build a mini module >> that exports all definitions I need from AppKit and Foundation). > > How long does it normally take to launch an app of similar complexity > on your machine? Project Builder starts in about 5 seconds (Powerbook G5 667 Mhz, 512 MB). > > The period of time between NSApplicationMain() and the first > awakeFromNib() is comprised of most of the "hard stuff" of launching > an app; setting up the notification stuff, the mach ports, and all > the other innards that integrate the App into OS X.... > > Unless we have a bug somewhere, that period of time shouldn't really > be any longer than a 'normal' app as there really isn't anything from > PyObjC that is involved (unless your app delegate implements > willFinishLaunching())? I don't implement an app delegate at all. Ronald |
From: Zachery B. <zb...@ur...> - 2003-03-21 19:44:51
|
On Friday, Mar 21, 2003, at 13:02 US/Eastern, Ronald Oussoren wrote: > Project Builder starts in about 5 seconds (Powerbook G5 667 Mhz, 512 > MB). Ooh, when do I get one? :) Zac |
From: Ronald O. <ous...@ci...> - 2003-03-22 06:57:42
|
On Friday, Mar 21, 2003, at 20:26 Europe/Amsterdam, Zachery Bir wrote: > On Friday, Mar 21, 2003, at 13:02 US/Eastern, Ronald Oussoren wrote: > >> Project Builder starts in about 5 seconds (Powerbook G5 667 Mhz, 512 >> MB). > > Ooh, when do I get one? :) Oops, I shouldn't have mentioned the G5. I hope my NDA doesn't get revoked because of this :-) Ronald |
From: <bo...@pa...> - 2003-03-21 17:09:28
|
only if you never want to change the name. --bob > How about PyObjCTools? > > - module PyObjCTools.Conversion would be the new name of > Foundation.Conversion > - module PyObjCTools.AppHelper would contain your function and other > NSApplication related tools |
From: Just v. R. <ju...@le...> - 2003-03-21 17:17:24
|
Ronald Oussoren wrote: > I think this would be very usefull. However, I think this module > should be in a seperate package to make it clear that this is a > PyObjC helper module instead of wrapped Cocoa functionality. > Foundation.Conversion should be moved to this package as well. > > How about PyObjCTools? Fine with me. > - module PyObjCTools.Conversion would be the new name of > Foundation.Conversion > - module PyObjCTools.AppHelper would contain your function and other > NSApplication related tools Ok. > Speaking of NSApplicationMain... Does anyone care to offer on opinion > on the enormous amount of time between the call to NSApplicationMain > and the call to awakeFromNib in a simple application? That period of > time takes about 4 seconds of the 5 seconds my application needs to > start up I'm also still seeing long startup times, about 8-10 seconds. I'll measure later how much of that is spent between NSApplicationMain() and the first awakeFromNib call. > (using a savagely butchered version of PyObjC, 'import > AppKit' would use another 2 seconds if I hadn't build a mini module > that exports all definitions I need from AppKit and Foundation). Does this mean we should revisit the idea of replacing the Cocoa modules with "lazy" module-like objects? If *you* are butchering up PyObjC that should be a sign... Just |
From: Ronald O. <ous...@ci...> - 2003-03-21 20:13:48
Attachments:
tm.m
TableModel.py
|
On Friday, Mar 21, 2003, at 18:02 Europe/Amsterdam, Just van Rossum wrote: > >> Speaking of NSApplicationMain... Does anyone care to offer on opinion >> on the enormous amount of time between the call to NSApplicationMain >> and the call to awakeFromNib in a simple application? That period of >> time takes about 4 seconds of the 5 seconds my application needs to >> start up > > I'm also still seeing long startup times, about 8-10 seconds. I'll > measure later how much of that is spent between NSApplicationMain() and > the first awakeFromNib call. Part of the time is in bootstrapping Python, not much we can do about that (and not part of the 5 seconds I mentioned earlier). > >> (using a savagely butchered version of PyObjC, 'import >> AppKit' would use another 2 seconds if I hadn't build a mini module >> that exports all definitions I need from AppKit and Foundation). > > Does this mean we should revisit the idea of replacing the Cocoa > modules > with "lazy" module-like objects? If *you* are butchering up PyObjC that > should be a sign... As a datapoint (PB 667Mhz) I've done some crude experiments using the TableModel example (Python 2.3): * Plain example starts in 6 bounces * Objective-C version of same starts in 2 bounces * Simplified AppKit/Foundation starts in 4 bounces I've attached the source code for the modified versions. Building these is not too hard: Call buildapp.py to create the plain version. For the Objective-C version compile tm.m and use that to replace Contents/MacOS/TableModel. For the simplified version copy TableModel.py to Contents/Resources and then copy _AppKit*, NibClassBuilder.py* and _Foundation* from the site-packages directory to Contents/Resources. Some further investigation shows lots (about 8000) of calls to get_class_info in objc-class.m between the call to NSApplicationMain and the call to awakeFromNib. Removing these calls using the new PyHeapTypeObject to store the additional information in the class proxy instead of a seperate datastructure doesn't reduce the startup time for me. It would have made live much easier if this had been available in 2.2 :-). I'll probably add this change to CVS if I can do so without negative effects on the source code. Ronald P.S. bundlebuilder.py from the current Python CVS doesn't work for me, I get #!python2.3 at the start of the bootstrap script. That's probably because 'which python' is '/usr/local/bin/python' and that is a symlink to 'python2.3' in the same directory. This fragment of code doesn't work correctly with that: hashbang = sys.executable while os.path.islink(hashbang): hashbang = os.readlink(hashbang) The second time through the loop hashbang is 'python2.3', which doesn't exist. The assignment should probably be something like 'os.path.join(os.dirname(hashbang), os.readlink(hashbang))' |
From: Just v. R. <ju...@le...> - 2003-03-21 20:40:10
|
Ronald Oussoren wrote: > P.S. bundlebuilder.py from the current Python CVS doesn't work for me, > I get #!python2.3 at the start of the bootstrap script. That's probably > because 'which python' is '/usr/local/bin/python' and that is a > symlink to 'python2.3' in the same directory. This fragment of code > doesn't work correctly with that: > hashbang = sys.executable > while os.path.islink(hashbang): > hashbang = os.readlink(hashbang) > > The second time through the loop hashbang is 'python2.3', which > doesn't exist. The assignment should probably be something like > 'os.path.join(os.dirname(hashbang), os.readlink(hashbang))' Beh -- but thanks for reporting it! I changed it (reluctantly) from #!/user/bin/python to what you see now by Jack's request, basically so the same interpreter is used for bootstrapping and running the applet (if it's a standalone app we use /usr/bin/python). It seems a micro optimization that caused more trouble than it's worth. Unless I get some more arguments for using an improved readlink approach, I'll go back to hardcoding it again, or possibly using #!/usr/bin/env python. Just |
From: Jack J. <Jac...@or...> - 2003-03-21 22:05:00
|
On vrijdag, maa 21, 2003, at 21:12 Europe/Amsterdam, Ronald Oussoren wrote: > P.S. bundlebuilder.py from the current Python CVS doesn't work for me, > I get #!python2.3 at the start of the bootstrap script. That's > probably because 'which python' is '/usr/local/bin/python' and that > is a symlink to 'python2.3' in the same directory. This fragment of > code doesn't work correctly with that: > hashbang = sys.executable > while os.path.islink(hashbang): > hashbang = os.readlink(hashbang) > > The second time through the loop hashbang is 'python2.3', which > doesn't exist. The assignment should probably be something like > 'os.path.join(os.dirname(hashbang), os.readlink(hashbang))' Don't use this construct, use os.path.realpath(sys.executable). It follows recursive symlinks and understands about relative symlinks too. -- - Jack Jansen <Jac...@or...> http://www.cwi.nl/~jack - - If I can't dance I don't want to be part of your revolution -- Emma Goldman - |
From: Just v. R. <ju...@le...> - 2003-03-21 22:25:22
|
Jack Jansen wrote: > > 'os.path.join(os.dirname(hashbang), os.readlink(hashbang))' > > Don't use this construct, use os.path.realpath(sys.executable). It > follows recursive symlinks and understands about relative symlinks > too. Ah, nice. Will patch bundlebuilder accordingly. Just |
From: Ronald O. <ous...@ci...> - 2003-03-22 16:46:16
|
On Friday, Mar 21, 2003, at 18:02 Europe/Amsterdam, Just van Rossum wrote: > >> (using a savagely butchered version of PyObjC, 'import >> AppKit' would use another 2 seconds if I hadn't build a mini module >> that exports all definitions I need from AppKit and Foundation). > > Does this mean we should revisit the idea of replacing the Cocoa > modules > with "lazy" module-like objects? If *you* are butchering up PyObjC that > should be a sign... I did this to investigate how much faster I could get the application without rewriting in Objective-C, which would not be worth it just for shaving a couple of seconds from the lauch-time. I wonder if there are a lot of problems where (re)writing in Objective-C instead of Python would be worthwile. That doesn't mean I'm not interested in speeding up PyObjC. I'm not much into import hacks, so how would one cause the creation of a custom module-like object when doing 'import AppKit'? Does this entrail creating an importhook, or is replacing sys.modules['AppKit'] from inside AppKit/__init__.py sufficient? Ronald |
From: Just v. R. <ju...@le...> - 2003-03-22 18:36:25
|
Ronald Oussoren wrote: > That doesn't mean I'm not interested in speeding up PyObjC. I'm not > much into import hacks, so how would one cause the creation of a > custom module-like object when doing 'import AppKit'? Does this > entrail creating an importhook, or is replacing sys.modules['AppKit'] > from inside AppKit/__init__.py sufficient? The latter is sufficient. Supporting __getattr__ and having an __all__ attribute is all that's needed, if I recall correctly. For a package directory it should have a __path__ attribute. Just |
From: Ronald O. <ous...@ci...> - 2003-03-22 19:04:50
|
On Saturday, Mar 22, 2003, at 19:36 Europe/Amsterdam, Just van Rossum wrote: > Ronald Oussoren wrote: > >> That doesn't mean I'm not interested in speeding up PyObjC. I'm not >> much into import hacks, so how would one cause the creation of a >> custom module-like object when doing 'import AppKit'? Does this >> entrail creating an importhook, or is replacing sys.modules['AppKit'] >> from inside AppKit/__init__.py sufficient? > > The latter is sufficient. Supporting __getattr__ and having an __all__ > attribute is all that's needed, if I recall correctly. For a package > directory it should have a __path__ attribute. Interesting, that's pretty easy. I'm going to experiment with this. I'm going to target two things: 1. Speedier initialisation 2. Make the NSApp global variable available as a variable instead of a function I foresee one problem: Dynamicly adding the class names in AppKit to the __all__ list of the AppKit module/package. The only method I can come up with right now is about as expensive as the current code, which is not very usefull. We could depricate 'from AppKit import *', but that doesn't make me much happier. The other alternative is staticly filling __all__ (possibly based on the OS version). Ronald |
From: Jack J. <Jac...@or...> - 2003-03-21 22:07:49
|
On vrijdag, maa 21, 2003, at 17:33 Europe/Amsterdam, Ronald Oussoren wrote: > Speaking of NSApplicationMain... Does anyone care to offer on opinion > on the enormous amount of time between the call to NSApplicationMain > and the call to awakeFromNib in a simple application? That period of > time takes about 4 seconds of the 5 seconds my application needs to > start up (using a savagely butchered version of PyObjC, 'import > AppKit' would use another 2 seconds if I hadn't build a mini module > that exports all definitions I need from AppKit and Foundation). Are you using Python from CVS? Python 2.3a2 still had prebinding enabled, which slowed down dynamic loading terribly (see the thread "Slow loading of modules" around 23-Feb-2003 in pythonmac-sig, for the people who weren't in on this). And if you are using a non-prebinding Python: try using "sample" on python while it is doing it's NSApplicationMain dance. -- - Jack Jansen <Jac...@or...> http://www.cwi.nl/~jack - - If I can't dance I don't want to be part of your revolution -- Emma Goldman - |
From: Ronald O. <ous...@ci...> - 2003-03-22 06:51:58
|
On Friday, Mar 21, 2003, at 23:07 Europe/Amsterdam, Jack Jansen wrote: > > On vrijdag, maa 21, 2003, at 17:33 Europe/Amsterdam, Ronald Oussoren > wrote: >> Speaking of NSApplicationMain... Does anyone care to offer on opinion >> on the enormous amount of time between the call to NSApplicationMain >> and the call to awakeFromNib in a simple application? That period of >> time takes about 4 seconds of the 5 seconds my application needs to >> start up (using a savagely butchered version of PyObjC, 'import >> AppKit' would use another 2 seconds if I hadn't build a mini module >> that exports all definitions I need from AppKit and Foundation). > > Are you using Python from CVS? Python 2.3a2 still had prebinding > enabled, which slowed down dynamic loading terribly (see the thread > "Slow loading of modules" around 23-Feb-2003 in pythonmac-sig, for the > people who weren't in on this). > > And if you are using a non-prebinding Python: try using "sample" on > python while it is doing it's > NSApplicationMain dance. I'm using Python from CVS. I didn't know about 'sample', it looks like very usefull tool. Thanks for the hint. Ronald |
From: Jack J. <Jac...@cw...> - 2003-03-24 16:04:01
|
On Thursday, Mar 20, 2003, at 15:23 Europe/Amsterdam, Just van Rossum wrote: > I've been using the following idiom to enter the Cocoa main event loop > for > some time now: > > > def unexpectedErrorAlert(): > exceptionInfo = > traceback.format_exception_only(*sys.exc_info()[:2])[0].strip() > return NSRunAlertPanel("An unexpected error has occurred", > "(%s)" % exceptionInfo, > "Continue", "Quit", None) > > > mainFunc = NSApplicationMain > args = (sys.argv,) > while 1: > try: > mainFunc(*args) > except: > traceback.print_exc() > if not unexpectedErrorAlert(): > break > mainFunc = NSApp().run > args = () > else: > break Just, I modified this a bit, by putting everything, including all needed imports, into a main() program. Also, in stead of printing the traceback before calling unexpectedErrorAlert() I call raise in stead of break, this keeps the console log clean if the user decides to continue and it also enables debugging with the standard trick of setting PYTHONINSPECT=1 before you run your program, and then doing "import pdb; pdb.pm()" after the program has crashed. Here it is: def main(): from AppKit import NSApplicationMain, NSRunAlertPanel, NSApp import traceback import sys def unexpectedErrorAlert(): exceptionInfo = traceback.format_exception_only(*sys.exc_info()[:2])[0].strip() return NSRunAlertPanel("An unexpected error has occurred", "(%s)" % exceptionInfo, "Continue", "Quit", None) mainFunc = NSApplicationMain args = (sys.argv,) while 1: try: mainFunc(*args) except: if not unexpectedErrorAlert(): raise mainFunc = NSApp().run args = () else: break if __name__ == '__main__': main() -- Jack Jansen, <Jac...@cw...>, http://www.cwi.nl/~jack If I can't dance I don't want to be part of your revolution -- Emma Goldman |