pyobjc-dev Mailing List for PyObjC (Page 296)
Brought to you by:
ronaldoussoren
You can subscribe to this list here.
| 2000 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(9) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2001 |
Jan
(1) |
Feb
(2) |
Mar
(3) |
Apr
(30) |
May
(18) |
Jun
|
Jul
(4) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2002 |
Jan
(7) |
Feb
(2) |
Mar
(1) |
Apr
|
May
|
Jun
(3) |
Jul
(13) |
Aug
|
Sep
(23) |
Oct
(180) |
Nov
(291) |
Dec
(95) |
| 2003 |
Jan
(338) |
Feb
(352) |
Mar
(97) |
Apr
(46) |
May
(226) |
Jun
(184) |
Jul
(145) |
Aug
(141) |
Sep
(69) |
Oct
(161) |
Nov
(96) |
Dec
(90) |
| 2004 |
Jan
(66) |
Feb
(87) |
Mar
(98) |
Apr
(132) |
May
(115) |
Jun
(68) |
Jul
(150) |
Aug
(92) |
Sep
(59) |
Oct
(52) |
Nov
(17) |
Dec
(75) |
| 2005 |
Jan
(84) |
Feb
(191) |
Mar
(133) |
Apr
(114) |
May
(158) |
Jun
(185) |
Jul
(62) |
Aug
(28) |
Sep
(36) |
Oct
(88) |
Nov
(65) |
Dec
(43) |
| 2006 |
Jan
(85) |
Feb
(62) |
Mar
(92) |
Apr
(75) |
May
(68) |
Jun
(101) |
Jul
(73) |
Aug
(37) |
Sep
(91) |
Oct
(65) |
Nov
(30) |
Dec
(39) |
| 2007 |
Jan
(24) |
Feb
(28) |
Mar
(10) |
Apr
(2) |
May
(18) |
Jun
(16) |
Jul
(21) |
Aug
(6) |
Sep
(30) |
Oct
(31) |
Nov
(153) |
Dec
(31) |
| 2008 |
Jan
(63) |
Feb
(70) |
Mar
(47) |
Apr
(24) |
May
(59) |
Jun
(22) |
Jul
(12) |
Aug
(7) |
Sep
(14) |
Oct
(26) |
Nov
(5) |
Dec
(5) |
| 2009 |
Jan
(10) |
Feb
(41) |
Mar
(70) |
Apr
(88) |
May
(49) |
Jun
(62) |
Jul
(34) |
Aug
(15) |
Sep
(55) |
Oct
(40) |
Nov
(67) |
Dec
(21) |
| 2010 |
Jan
(60) |
Feb
(17) |
Mar
(26) |
Apr
(26) |
May
(29) |
Jun
(4) |
Jul
(21) |
Aug
(21) |
Sep
(10) |
Oct
(12) |
Nov
(3) |
Dec
(19) |
| 2011 |
Jan
(3) |
Feb
(13) |
Mar
(8) |
Apr
(8) |
May
(17) |
Jun
(20) |
Jul
(21) |
Aug
(7) |
Sep
|
Oct
|
Nov
(9) |
Dec
(11) |
| 2012 |
Jan
(3) |
Feb
|
Mar
|
Apr
(5) |
May
(4) |
Jun
(14) |
Jul
(5) |
Aug
(2) |
Sep
(15) |
Oct
(2) |
Nov
(23) |
Dec
(1) |
| 2013 |
Jan
(8) |
Feb
(1) |
Mar
|
Apr
|
May
(5) |
Jun
(1) |
Jul
(5) |
Aug
(4) |
Sep
|
Oct
(12) |
Nov
(10) |
Dec
(3) |
| 2014 |
Jan
(7) |
Feb
(14) |
Mar
(2) |
Apr
|
May
(2) |
Jun
(11) |
Jul
(10) |
Aug
(4) |
Sep
|
Oct
(8) |
Nov
(1) |
Dec
(2) |
| 2015 |
Jan
(9) |
Feb
(7) |
Mar
(1) |
Apr
|
May
(7) |
Jun
|
Jul
(5) |
Aug
(6) |
Sep
|
Oct
(1) |
Nov
(4) |
Dec
|
| 2016 |
Jan
(1) |
Feb
(1) |
Mar
(4) |
Apr
(2) |
May
(1) |
Jun
|
Jul
(6) |
Aug
(8) |
Sep
(21) |
Oct
(17) |
Nov
|
Dec
(36) |
| 2017 |
Jan
(6) |
Feb
(2) |
Mar
(4) |
Apr
(2) |
May
|
Jun
|
Jul
(1) |
Aug
|
Sep
(1) |
Oct
|
Nov
(1) |
Dec
(6) |
| 2018 |
Jan
(2) |
Feb
(3) |
Mar
(3) |
Apr
(14) |
May
(2) |
Jun
(2) |
Jul
(4) |
Aug
(3) |
Sep
(6) |
Oct
(16) |
Nov
(1) |
Dec
(6) |
| 2019 |
Jan
(3) |
Feb
(1) |
Mar
|
Apr
|
May
|
Jun
|
Jul
(2) |
Aug
|
Sep
|
Oct
(6) |
Nov
|
Dec
|
| 2020 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
(2) |
Jun
(1) |
Jul
(7) |
Aug
(1) |
Sep
(1) |
Oct
|
Nov
(2) |
Dec
(1) |
| 2021 |
Jan
(1) |
Feb
|
Mar
|
Apr
|
May
|
Jun
(2) |
Jul
|
Aug
(5) |
Sep
(1) |
Oct
|
Nov
(1) |
Dec
|
| 2023 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
|
Jun
|
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
|
Dec
|
| 2025 |
Jan
(2) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
(2) |
|
From: Bill B. <bb...@co...> - 2002-10-25 14:59:03
|
On Friday, October 25, 2002, at 05:22 AM, Ronald Oussoren wrote:
> On Thursday, Oct 24, 2002, at 23:40 Europe/Amsterdam, Bill Bumgarner
> wrote:
>> ... which is exactly a 1 for 1 correspondance to calls to alloc()
>> from within Python. I had been writing my code like...
>>
>> toolbarItem =
>> NSToolbarItem.alloc().initWithItemIdentifier_(anIdentifier)
>> toolbarItem.autorelease()
>>
>> ... but under the code implied by the -release above, I should not be
>> calling autorelease(). Makes sense.
> That was my idea: Conversion from Objective-C to Python implies
> removing all calls to 'retain', 'release' and 'autorelease'.
In general, this is great. I'm still concerned about the differences
in behavior that it implies when it only affects a handful of methods.
>> However, I'm not 100% sure that it is the correct pattern. I.e. do
>> we really want to make the bridge responsible for keeping track of
>> every single method that causes object allocation and do the -release
>> as the current code does?
> Yes, this makes the bridge as transparent as possible. Asking the
> programmer to *sometimes* worry about reference counts is confusing. I
> added the code above to PyObjC to make sure I never have to think
> about reference counts when programming in Python.
The developer still has to sometimes worry about reference counts and
has to do so in a fashion that doesn't come naturally [in my
experience].
We are really talking about a couple of different behaviors.
First, there is the behavior that an assignment or set membership
within Python implies a -retain. To balance the implied -retain,
removal from a set or destruction of the assignment implies a -release.
This seems to come naturally to most developers and is quite consistent
across the environment.
--
The second behavior is that any call to +alloc, +allocWithZone:, +copy,
+copyWithZone:, and +mutableCopyWithZone: implies a -release as the
object comes into the Python environment. This changes the meaning of
these methods in Python-- the developer can no longer look up the
documentation of the method and believe what Apple has to say!
Instead, the developer has to effectively raise a mental exception and
remember that these methods no longer mean what they used to mean.
I'm [very likely] the first person to use this feature besides you and
look at the problems it caused me! I was expected those methods to
behave exactly as they do in ObjC, but they didn't and the resulting
behavior was-- as is often the case with retain/release/autorelease
bugs-- incredibly difficult to track down and fix.
Even now that I'm familiar with the behavior, I'm still want to write
the same [[[... alloc] init] autorelease] idiom that is the defined
standard within Obj-C.
The implementation also makes the assumption that those are the only
methods that return retained instances of an object-- the only methods
that transfer ownership. It even mentions...
# These 5 are documented in Apple's Objective-C book, in theory these
# are the only methods that transfer ownership.
... but, already, there is a discrepancy! The +new method also
transfers ownership -- it is just a cover for [[... alloc] init].
Speaking of the +new method, a common design pattern is to implement
factory methods of the form +newWith... or +newBy... or +newFrom... or
+new* where the ... or * is replaced by some indication as to how the
newly created object shoudl be configured. All of these methods
return alloc/init'd instances of the object and all transfer ownership
to the caller.
And what about the different forms of copy? -deepCopy and
-deepMutableCopy immediately come to mind. As well, I have seen (and
occasionally written) convenience methods that do things like copy the
contents of an NSImage, returning a new NSImageRep that is -retain'd or
copied the contents of an NSTextView and returned a -retain'd
NSTextStorage.
Now the developer has to remember the 5 methods that are different [6,
really] and they have to remember that any other method that 'transfers
ownership' works the old way.
> Due to a 'feature' of NSOutlineView I still have to worry about this,
> but that is a different story. In short: NSOutlineView doesn't
> 'retain' the results of outlineView:child:ofItem:, but does hang on to
> this value. This means you cannot use python objects as return values,
> because their proxies are autoreleased and NSOutlineView tries to
> access these proxies after they are released... IMNSHO this is a bug
> and I'm thinking of fileing a bug report on this.
It is expected it to survive beyond the end of the current pass through
the main event loop? That's definitely broken.
I.e. this'll break it?
- outlineView:child:ofItem:
{
return [NSString stringWithFormat: @"%d %d", 1, 2"];
}
>> ... extra details deleted ...
>> This subtly, but significantly, changes the object lifespan patterns
>> pervasive throughout the foundation. While this particular example
>> is somewhat bogus, there are other situations where such a lifespan
>> change could cause a failure. Example: the developer creates a
>> class whose instances listen for a particular notification and remove
>> themselves as observers when dealloc'd. A common idiom for creation
>> might be MyChangeListener.alloc().init().autorelease(), but that
>> isn't possible with the current implementation of the bridge.
> 'a common idiom for creation MIGHT be': Is this really a common idiom?
> I'd rather not worry about theoretical changes. As far as I can judge
> right now the current policy of fully automaticly managing reference
> counts is far more usefull than manually updating them.
Common? No, but it is a pattern that I use and have encountered in
looking at other developer's code (several times throughout my career,
I have been in a developer support role).
I have been building developer tools for a long, long time -- one of
the lessons that has been beat into my thick skull over the years is
that I will never be able to predict how the community of developers
using my code is going to use that code. If I can imagine it, there
are probably 10 other things I didn't imagine that some developer will
try to do!
> BTW. If you really want to use autorelease you could always do
> 'obj.retain();obj.autorelease()'. This is different from the
> Objective-C way of doing this, but at least it is clear you're doing
> something fishy. And this is pretty fishy: In most programs 'at least
> as long as the current threads current autorelease pool' is 'at least
> until the start of the next round throught the event loop', which is
> probably a very short time.
Event loops aren't just for processing user events. In WebObjects
(which used to be an Obj-C app), one pass through the event loop
involved parsing and responding to an entire HTTP request. Now that
Web Services are all the rage, handling an HTTP request inside of a
Cocoa application is gaining in popularity and the most common way to
do so is using a similar one-request-per-pass-through-run-loop model
(this is exactly what I'm doing in two of my apps now).
Handling an HTTP request is a considerably more complex process than
handling a keystroke. To further compound the complexity, there is
often some kind of a persistent backing store that may have to be read
from and/or written to.
Instead of validating just one field of user input, an HTTP request may
have to validate a couple of dozen hunks of user input all at once.
If this is through to a backing store of some kind, there is typically
several layers of delegated validation occurring during the whole
process.
Because there is often some concept of a session -- roughly equivalent
to a Document -- there is often change notifications flying around the
system and listeners of change notifications.
Run loops are also used to do other kinds of 'event' processing;
calculation engines, data gathering, timed event, background processes,
distributed objects, etc...
> BTW2. I really don't like 'invisible' objects like the
> MyChangeListener example. How am I as a code maintainer/reviewer to
> know that this is the intended behaviour and not an attempt to work
> around another bug (at least this doesn't leak memory).
The 'invisible' object is a design pattern found throughout the AppKit,
the Foundation, EOF, and a number of other object oriented kits.
NSNotificationCenter is at the heart of it. As something -- an
application, a athread, a whatever -- is initialized, various random
objects have the opportunity to act as notification listeners. The
objects posting the notifications don't have any clue what, if
anything, is listening for the notifications.
To put this into context; say, I create a bundleized app-- Project
Builder's PBX Bundles and Interface Builder's Palettes are both good
examples. Within that app within a single run loop, I post a
notification of 'prepare to change object graph', followed by a series
of 'changing object graph' notifications' and -- if everything went
well -- a 'done changing the object graph' notification. Now, during
one of the 'changing object graph' notifications, an exception is
raised and the 'done changing' notification never gets posted.
A bundle author may create a bundle that listens for the 'prepare'
notifications. When received, that bundle then creates a listener for
'changing' and 'done' notifications that is highly customized to the
userInfo dictionary contents passed in the 'prepare' notification.
In this case, the only way the object will be deallocated is if it is
in the autorelease pool!
(I ran into this situation when writing an IB palette recently)
>> As well, there is no way we can ever quantify all possible methods
>> that a developer might use to produce a freshly allocated object that
>> the current code would need to -release to preserve the pattern as it
>> exists now.
> Yes we can. Apple clearly documents in the Objective-C reference
> manual that only a small number of methods should return objects that
> you don't have to 'retain' (basicly alloc and copy). If you get an
> object in any other way you should call 'retain' yourself. As much as
> a dislike the idea of 'autorelease' it does solve the problem of
> object ownership (why couldn't Objective-C use a real garbage
> collector and do away with manual memory managment?).
As indicated above, the documentation is wrong. As well, any
developer can come along and create new methods (the +new...: pattern
is actually quite useful) that the API will not be aware of.
The retain/release implied by assignment and set membership is that it
is ubiquitous to messaging through the bridge. Modifying the behavior
of a handful of methods is not ubiquitous and, as such, will never
achieve 100% coverage-- this will lead to confusion and bugs.
(Because C is a pointer based language, an Obj-C garbage collector--
there are a few available-- can never achieve 100% coverage....)
> If a developer writes classes that don't follow this convention that
> is a bug, plain and simple. Such code is even a problem for plain
> Objective-C users: Given the text in the Objective-C manual users will
> expect they have to retain the result of method calls, a method that
> does not follow this convention is confusing and is bound to introduce
> memory leaks.
>
> As should be obvious by now I am not convinced at all that the current
> policy of PyObjC is wrong. I'd like to see a real-live example of why
> the current policy is bad before I change my position.
Examples -- all culled from code that I have encountered recently
(names changed to protect the guilty):
foo = [NSObject new]
bar = [someObject deepCopy]
baz = [myDictionary deepMutableCopy]
bob = [aCollection copyWithConvertedValues]
Scanning my source tree-- a tree containing piles of my own code, code
of my clients, and code from numerous third parties-- using...
find . -name '*.m' -exec grep -H -e '+.*new.*' {} \;
I find about 20 instances where I or others have implemented a +new...
method that returns a retained object. That is just looking for
+new... and does not include the other cases where a developer decided
that they-- for whatever reason, some may be invalid-- wanted to return
a retained instances of something.
Searching through /Developer/Examples/ turns up a couple of examples
where +new... is used in a fashion that does not return a -retain'd
instance. So much for consistency...
b.bum
|
|
From: Jack J. <Jac...@cw...> - 2002-10-25 13:31:36
|
Bill, a couple of questions on your architectural remarks. On Friday, Oct 25, 2002, at 02:40 Europe/Amsterdam, Bill Bumgarner wrote: > Bugs & Architecture: > > - there should be a way in the GUI to open a file into the > settings -- i.e. the Open... command should be "Run script..." and > "Open..." should truly open the script as a document. [...] > > - the 'run in terminal' button doesn't update the field contents > and, as such, the contents can get out of sync... > > - If the multi-doc architecture is going to be used, there should > be something to save! [...] > Finally, the app really isn't a multiple-document type app. [...] All these hinge on the fact that PythonLauncher looks like a multidocument application, but really isn't. Is that a problem? I modeled the semantics more-or-less after Stuffit Expander: PythonLauncher can operate in 2 modes, one where it's already running (and opening a document always goes into interactive mode) and another one where the application is started for a single document, which is launched (optionally after allowing interaction to set the options) after which PythonLauncher quits without further ado. > - If I use the new menu item, it doesn't seem possible to set the > script to be executed by the document. That's because I haven't been able to disable the New command, which is what I really would want to do. Can you tell me how to do this? (Or, better, tell me where I should have found the information) > - Since both the preferences window and the documents have the > same interior UI, the NIB file for that UI should be split out and the > preferences and document window should share that single NIB. The > preferences window should likely move to a tabbed view with the > different file types in the tabs -- that seems to be the HIG way these > days. Again, I wanted to do this but I haven't been able to come up with a way to do it. Please enlighten me. There's actually two things that I haven't been able to find out: 1. How can I share the interior contents of two windows? 2. How do I get a tabbed view without actually having something different on every tab (I want the same controls on each tab, really, and I don't want to duplicate either the code or the controls)? > - NSTask would be a better candidate than system() to subshell out > the command. It would allow the app to monitor the output and display > a console. [...] > The first step in moving to a true multi-doc style app is for the app > to give the user control over when documents are closed -- that is, if > a user opens a python script [as opposed to Running... a script > without opening a visible document at all], the associated window > should remain open until the user tells it to go away. The document > should have a notion of 'currently running, do you want to launch > another instance of the same thing?' and 'abort/stop current run'. These are things I specifically don't want to do (i.e. convince me otherwise if you think it's worthwhile). The intention of PythonLauncher is to be like Java AppletLauncher (or the above mentioned Stuffit Expander): it should "fire and forget" the script. Remember, the reason this was written in the first place is because CFBundleTypeRole=Shell doesn't work (which is what it's trying to emulate, with the options dialog thrown in so that we no longer have to provide that in pythonw, and have the functionality for console scripts too). Ideally a naive user shouldn't even be aware of the fact that this thing exists. -- - 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...> - 2002-10-25 11:08:48
|
On Thursday, Oct 24, 2002, at 02:01 Europe/Amsterdam, bb...@ma... wrote: > As Ronald indicated, you have to manually create the subclass of > NSObject (or whatever) in IB. Unfortunately, we don't currently have > a "create source files" that'll create Python source from within IB. > Well, maybe not true -- I saw in the repository that Ronald has > another bit of magic in the works. :-) This 'magic' is already in the installer on your site, I just haven't wired it up yet. And now on to a description of this magic feature... When you define a user-interface in Interface Builder one of the files that is written into the NIB named 'classes.nib'. This is a text file containing a description of the classes that are defined in the NIB. I've written a python module that can be used to read this file and generate a module containing class definitions. In your own code you just import this module and subclass from the generated classes to define the classes from the NIB. Advantages are: - No need to manually define 'IBOutlet' entries (outlets in Interface Builder) - No need to manually specify the signature for actions - No manual sync between definitions in IB and python code. I'll probably change one or more of the examples to use this script, but that will have to wait until we've fixed some problems with the C code and have written some more tests and documentation. Ronald |
|
From: Ronald O. <ous...@ci...> - 2002-10-25 09:22:53
|
On Thursday, Oct 24, 2002, at 23:40 Europe/Amsterdam, Bill Bumgarner
wrote:
> I noticed that Web Services Tool stopped working recently. In
> delving into it, I had to comment out the -release here...
>
> if (res && ObjCObject_Check(res) && self->sel_allocator) {
> /* Ownership transfered to us, but 'execute' method has
> * increased retainCount, the retainCount is now one to high
> */
> NSLog(@"punting a release... %@", ObjCObject_GetObject(res));
> // [ObjCObject_GetObject(res) release];
> }
> return res;
>
[... Output that explains what code above does]
>
> ... which is exactly a 1 for 1 correspondance to calls to alloc() from
> within Python. I had been writing my code like...
>
> toolbarItem =
> NSToolbarItem.alloc().initWithItemIdentifier_(anIdentifier)
> toolbarItem.autorelease()
>
> ... but under the code implied by the -release above, I should not be
> calling autorelease(). Makes sense.
That was my idea: Conversion from Objective-C to Python implies
removing all calls to 'retain', 'release' and 'autorelease'.
>
> However, I'm not 100% sure that it is the correct pattern. I.e. do we
> really want to make the bridge responsible for keeping track of every
> single method that causes object allocation and do the -release as the
> current code does?
Yes, this makes the bridge as transparent as possible. Asking the
programmer to *sometimes* worry about reference counts is confusing. I
added the code above to PyObjC to make sure I never have to think about
reference counts when programming in Python.
Due to a 'feature' of NSOutlineView I still have to worry about this,
but that is a different story. In short: NSOutlineView doesn't 'retain'
the results of outlineView:child:ofItem:, but does hang on to this
value. This means you cannot use python objects as return values,
because their proxies are autoreleased and NSOutlineView tries to
access these proxies after they are released... IMNSHO this is a bug
and I'm thinking of fileing a bug report on this.
>
> I don't think we do because it introduces a situation where Obj-C
> written in Python behaves artificially different than Obj-C in Obj-C.
>
> Consider the following hunk of code under the conditions where the
> -release in the above code snippet doesn't happen:
>
> if aMenu:
> menuItem = NSMenuItem.alloc().init().autorelease()
> menuItem.setSubmenu_(aMenu)
> menuItem.setTitle_( aMenu.title() )
>
> The developer is explicitly indicating that menuItem should live for
> at least as long as the current thread's current autorelease pool.
>
> Now, consider the same code without the autorelease, but with the
> implied -release to balance the +alloc as the code from objc_selcall()
> currently exists:
>
> if aMenu:
> menuItem = NSMenuItem.alloc().init()
> menuItem.setSubmenu_(aMenu)
> menuItem.setTitle_( aMenu.title() )
>
> In this case, the NSMenuItem instance will only live as long as
> menuItem remains in scope. As soon as the scope of the if statement
> is torn down, menuItem will be -released and destroyed.
>
> This subtly, but significantly, changes the object lifespan patterns
> pervasive throughout the foundation. While this particular example is
> somewhat bogus, there are other situations where such a lifespan
> change could cause a failure. Example: the developer creates a class
> whose instances listen for a particular notification and remove
> themselves as observers when dealloc'd. A common idiom for creation
> might be MyChangeListener.alloc().init().autorelease(), but that isn't
> possible with the current implementation of the bridge.
'a common idiom for creation MIGHT be': Is this really a common idiom?
I'd rather not worry about theoretical changes. As far as I can judge
right now the current policy of fully automaticly managing reference
counts is far more usefull than manually updating them.
BTW. If you really want to use autorelease you could always do
'obj.retain();obj.autorelease()'. This is different from the
Objective-C way of doing this, but at least it is clear you're doing
something fishy. And this is pretty fishy: In most programs 'at least
as long as the current threads current autorelease pool' is 'at least
until the start of the next round throught the event loop', which is
probably a very short time.
BTW2. I really don't like 'invisible' objects like the MyChangeListener
example. How am I as a code maintainer/reviewer to know that this is
the intended behaviour and not an attempt to work around another bug
(at least this doesn't leak memory).
>
> As well, there is no way we can ever quantify all possible methods
> that a developer might use to produce a freshly allocated object that
> the current code would need to -release to preserve the pattern as it
> exists now.
Yes we can. Apple clearly documents in the Objective-C reference manual
that only a small number of methods should return objects that you
don't have to 'retain' (basicly alloc and copy). If you get an object
in any other way you should call 'retain' yourself. As much as a
dislike the idea of 'autorelease' it does solve the problem of object
ownership (why couldn't Objective-C use a real garbage collector and do
away with manual memory managment?).
If a developer writes classes that don't follow this convention that is
a bug, plain and simple. Such code is even a problem for plain
Objective-C users: Given the text in the Objective-C manual users will
expect they have to retain the result of method calls, a method that
does not follow this convention is confusing and is bound to introduce
memory leaks.
As should be obvious by now I am not convinced at all that the current
policy of PyObjC is wrong. I'd like to see a real-live example of why
the current policy is bad before I change my position.
Ronald
|
|
From: Jack J. <Jac...@or...> - 2002-10-25 08:00:54
|
On Friday, October 25, 2002, at 02:40 , Bill Bumgarner wrote: > Your skin is thick, you know you're a damned good at this stuff, > and I don't have a lot of time -- as such, I'm not going to candy coat > anything... just try to stick with terse [hopefully] constructive > criticism in the form of a basic code /architecture review. Bill, thanks a *lot*! This style of comment is very helpful to me when exploring a new language. I'll have another look at the code and try to bring it more in line with what you suggest, -- - 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: Bill B. <bb...@co...> - 2002-10-25 01:34:17
|
On Thursday, October 24, 2002, at 04:47 PM, Jack Jansen wrote:
> On maandag, september 23, 2002, at 07:13 , Bill Bumgarner wrote:
>
>> I'll have to revisit Jack's PythonLauncher when I have more time-- it
>> has some serious issues/bugs.
>
> Bill,
> could you explain, please? PythonLauncher is my first "real" ObjC
> program, so I'm not surprised there's bug in it (let alone
> ideosyncracies: I couldn't find any example of how to mould the
> per-file settings based on preference settings in the Document
> paradigm, so I just came up with something). If you can give me some
> hints as to what I did wrong I'd love to hear it...
Jack,
It works -- which is more than can be said for a lot of first
programs written by developers working against an unfamiliar API!
A lot of the problems have to do with not following the patterns as set
forth in "traditional" ObjC/Cocoa programming -- not something that
someone new to the environment would necessarily find natural to their
development environment.
Your skin is thick, you know you're a damned good at this stuff,
and I don't have a lot of time -- as such, I'm not going to candy coat
anything... just try to stick with terse [hopefully] constructive
criticism in the form of a basic code /architecture review.
Low level code review (syntax, naming, etc -- i.e. nit picking):
The methods ...
- (IBAction)do_run:(id)sender;
- (IBAction)do_cancel:(id)sender;
- (IBAction)do_reset:(id)sender;
- (IBAction)do_apply:(id)sender;
... would be traditionally declared as (keeping the names the same, but
in the ObjC style) ...
- (IBAction)doRun:(id)sender;
- (IBAction)doCancel:(id)sender;
- (IBAction)doReset:(id)sender;
- (IBAction)doApply:(id)sender;
... the 'do' prefix is a useful distinction. It is equally as common
to see no prefix/suffix or to see 'Action:' as a suffix on each action
method (redundant in the declarations, damned handy -- like do* -- in
the code):
- (IBAction)runAction: sender;
- (IBAction)cancelAction: sender;
- (IBAction)resetAction: sender;
- (IBAction)applyAction: sender;
-
In the init method ...
- (id)init
{
[super init];
if (self) {
script = @"<no script>.py";
filetype = @"Python Script";
}
return self;
}
... the two instance variables need to be retained and the test for
(self) being defined will never fail even if super's init did fail.
Try...
- (id)init
{
self = [super init];
if (self) {
script = [@"<no script>.py" retain];
filetype = [@"Python Script" retain];
}
return self;
}
... or, alternatively, simply set script and filetype to nil (or, if
feeling particularly terse, don't set 'em at all because ObjC
initializes all variables to nil upon allocation -- personally, I like
to set it to nil simply as an acknowledgment that that is what I expect
the value to be).
-
The methods ...
- (void)load_defaults
- (void)update_display
- (void)update_settings
... would typically be declared as ...
- (void)loadDefaults
- (void)updateDisplay
- (void)updateSettings
... etc ...
-
In general, you should use -UTF8String to retrieve character buffers
from NSString objects to be passed off to the BSD layer. The BSD
layer should be able to handle UTF-8 encoded unicode for paths, etc,
throughout -- by doing this, the app will work on systems where the
app, python or the scripts are installed on a path that has unicode
characters in it.
-
In -readFromFile:....
script = [fileName retain];
filetype = [type retain];
Assuming the previous values of the iVar have been retained (the bug in
init has been fixed), this will leak the two string objects. Release
'em first.
-
FileSettings
Lots of hardcoded stuff throughout -- need to be able to support a
framework build of python in ~/Library/Frameworks/ or a command line in
/usr/bin/python, /usr/local/bin/ or /sw/bin (or wherever).
-
The interaction between -init, -initWithFileSettings: and
-factorySettingsForFileType: is a bit odd. If anything,
factorySettingsForFileType: should return void and -init should simply
invoke the method before returning self.
-factorySettingsForFileType: should probably be
-applyFactorySettingsForFileType: since it is more about changing the
instance and less about finding a settings set for a particular file
type (which would be the role of a class method).
-
-initWithFileSettings:
That style of indirection into the object (i.e. source->debug) is
extremely uncommon in ObjC. It breaks encapsulation.
Any method prefixed with -init is generally considered to be an
initializer and, as such, is likely going to be used in the pattern of
[[... alloc] init...] -- as such, the method should invoke [self init]
or [super init]...
---
In interacting with user defaults, I typically create a property list
file that contains the registration defaults for the application. This
file is added to the project's resources. To load the defaults is a
matter of...
... typically done in applicationDidFinishLaunching: or
applicationWillFinishLaunching: ...
NSDictionary *registrationDefaults = [NSDictionary
dictionaryWithContentsOfFile: [[NSBundle mainBundle] pathForResource:
@"RegistrationDefaults" ofType: @"plist"]];
[[NSUserDefaults standardUserDefaults] registerDefaults:
registrationDefaults];
This also makes it quite easy to restore all the way back to factory
settings without hardwiring the factory settings into the code --
simply grab the registration defaults from the file and set them into
the app's domain.
---
Bugs & Architecture:
- there should be a way in the GUI to open a file into the settings
-- i.e. the Open... command should be "Run script..." and "Open..."
should truly open the script as a document. Or maybe have an
accessory view with an "edit settings before running" checkbox?
- the 'run in terminal' button doesn't update the field contents
and, as such, the contents can get out of sync...
- NSTask would be a better candidate than system() to subshell out
the command. It would allow the app to monitor the output and display
a console. If motivated, you could even have a field via which to
interact with the interpreter (the current interactive mode doesn't do
much good if, say, the app were launched from the Finder).
- If the multi-doc architecture is going to be used, there should
be something to save! How about saving the current settings out to a
file such that the user can double-click the file to not only run the
script but run it with their custom set of settings?
- If I use the new menu item, it doesn't seem possible to set the
script to be executed by the document.
- +getDefaultsForFileType: should use a dictionary to store the
file settings.
- [[FileSettings new] init] == [[[FileSettings alloc] init] init]
- Since both the preferences window and the documents have the same
interior UI, the NIB file for that UI should be split out and the
preferences and document window should share that single NIB. The
preferences window should likely move to a tabbed view with the
different file types in the tabs -- that seems to be the HIG way these
days.
Finally, the app really isn't a multiple-document type app. The
comment above would move it in that direction, but there still wouldn't
be a concept of "documents" given the current implementation. As it
is, the app's name is indicative of it's role -- it launches Python
scripts and provides a bit of control over how they are executed. The
first step in moving to a true multi-doc style app is for the app to
give the user control over when documents are closed -- that is, if a
user opens a python script [as opposed to Running... a script without
opening a visible document at all], the associated window should remain
open until the user tells it to go away. The document should have a
notion of 'currently running, do you want to launch another instance of
the same thing?' and 'abort/stop current run'. Documents should be
able to be saved -- everything is pretty much there to do so now, just
a matter of returning an NSData containing the settings + the path to
the script from the appropriate NSDocument method.
...
My train has arrived at its destination, so I'm going to send this
now...
Given the direction that MacPython is going-- away from solely focused
on the framework build and to a form that works with Apple's build of
python or whatever happens to be present-- there are some other things
that should likely change about the GUI tools used to work with the
Python environment.
Of course, a part of me wants to rewrite PythonLauncher using PyObjC
:-).
b.bum
|
|
From: Bill B. <bb...@co...> - 2002-10-24 21:40:26
|
I noticed that Web Services Tool stopped working recently. In delving
into it, I had to comment out the -release here...
if (res && ObjCObject_Check(res) && self->sel_allocator) {
/* Ownership transfered to us, but 'execute' method has
* increased retainCount, the retainCount is now one to high
*/
NSLog(@"punting a release... %@", ObjCObject_GetObject(res));
// [ObjCObject_GetObject(res) release];
}
return res;
(at the end of objc_selcall())
In running the app, I see...
2002-10-24 17:03:20.535 Web Services Tool[16103] punting a release...
<WSTConnectionWindowController: 0xbd5de0>
2002-10-24 17:03:20.922 Web Services Tool[16103] punting a release...
<NSToolbar: 0xbb3940>
2002-10-24 17:03:20.974 Web Services Tool[16103] punting a release...
<NSToolbarItem: 0xbd7db0>
2002-10-24 17:03:21.008 Web Services Tool[16103] punting a release...
<NSToolbarItem: 0xc05f50>
2002-10-24 17:03:21.029 Web Services Tool[16103] punting a release...
<NSToolbarItem: 0xc01480>
2002-10-24 17:03:21.050 Web Services Tool[16103] punting a release...
<NSToolbarItem: 0xc08dd0>
2002-10-24 17:03:21.077 Web Services Tool[16103] punting a release...
<NSToolbarItem: 0xc04fd0>
... which is exactly a 1 for 1 correspondance to calls to alloc() from
within Python. I had been writing my code like...
toolbarItem =
NSToolbarItem.alloc().initWithItemIdentifier_(anIdentifier)
toolbarItem.autorelease()
... but under the code implied by the -release above, I should not be
calling autorelease(). Makes sense.
However, I'm not 100% sure that it is the correct pattern. I.e. do we
really want to make the bridge responsible for keeping track of every
single method that causes object allocation and do the -release as the
current code does?
I don't think we do because it introduces a situation where Obj-C
written in Python behaves artificially different than Obj-C in Obj-C.
Consider the following hunk of code under the conditions where the
-release in the above code snippet doesn't happen:
if aMenu:
menuItem = NSMenuItem.alloc().init().autorelease()
menuItem.setSubmenu_(aMenu)
menuItem.setTitle_( aMenu.title() )
The developer is explicitly indicating that menuItem should live for at
least as long as the current thread's current autorelease pool.
Now, consider the same code without the autorelease, but with the
implied -release to balance the +alloc as the code from objc_selcall()
currently exists:
if aMenu:
menuItem = NSMenuItem.alloc().init()
menuItem.setSubmenu_(aMenu)
menuItem.setTitle_( aMenu.title() )
In this case, the NSMenuItem instance will only live as long as
menuItem remains in scope. As soon as the scope of the if statement is
torn down, menuItem will be -released and destroyed.
This subtly, but significantly, changes the object lifespan patterns
pervasive throughout the foundation. While this particular example is
somewhat bogus, there are other situations where such a lifespan change
could cause a failure. Example: the developer creates a class whose
instances listen for a particular notification and remove themselves as
observers when dealloc'd. A common idiom for creation might be
MyChangeListener.alloc().init().autorelease(), but that isn't possible
with the current implementation of the bridge.
As well, there is no way we can ever quantify all possible methods that
a developer might use to produce a freshly allocated object that the
current code would need to -release to preserve the pattern as it
exists now.
The retain/release on Python assignment [or addition to a collection,
whatever] is definitely the correct thing to do. But that is about
the limit of what we can do automatically without opening ourselves up
for some serious inconsistency in the long run.
|
|
From: Jack J. <Jac...@or...> - 2002-10-24 20:47:55
|
On maandag, september 23, 2002, at 07:13 , Bill Bumgarner wrote: > I'll have to revisit Jack's PythonLauncher when I have more > time-- it has some serious issues/bugs. Bill, could you explain, please? PythonLauncher is my first "real" ObjC program, so I'm not surprised there's bug in it (let alone ideosyncracies: I couldn't find any example of how to mould the per-file settings based on preference settings in the Document paradigm, so I just came up with something). If you can give me some hints as to what I did wrong I'd love to hear it... -- - 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: Bill B. <bb...@co...> - 2002-10-24 14:32:15
|
(Another "debugging out loud" session -- but this one ended happily.
I'll put the 'solution' first and those interested can read down for
the details that led to it...)
#if 1
self_obj = nil;
if (*[methinfo methodReturnType] == _C_ID) {
[inv setReturnValue:&self_obj];
}
[inv setTarget:self_obj];
[inv setArgument:&self_obj atIndex:0];
#endif
[inv release];
[methinfo release];
inv = nil;
The above is found in objc_support.m around line 1423. By changing
the "#if 1" to an "#if 0", the bug goes away and all unit tests pass
(except invocation of class methods when an instance method of the same
name also exists).
The code #if'd in/out looks to be there to force the NSInvocation to
clean up after itself. In theory, that should be handled by the [inv
-release] on the line immediately after that block of code. In
practice, this still shouldn't cause a crash assuming that the
NSInvocation only retains things that it releases.
Why the crash was happening isn't clear to me. The [lack of the
above] code may also potentially be causing a memory leak?
Change committed.
---
I believe I have tracked the crasher w/the collection classes down to
being related to the following behavior of NSArray:
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *a;
a = [NSMutableArray alloc];
NSLog(@"0x%x", a);
a = [a init];
NSLog(@"0x%x", a);
a = [NSMutableArray alloc];
NSLog(@"0x%x", a);
a = [a init];
NSLog(@"0x%x", a);
[pool release];
return 0;
}
The above outputs -- of note, If I change the second [NSMutableArray
alloc] to [NSArray alloc], the output remains *unchanged*.
2002-10-24 09:35:37.398 barfoo[19606] 0x4b580
2002-10-24 09:35:37.424 barfoo[19606] 0x57c90
2002-10-24 09:35:37.439 barfoo[19606] 0x4b580
2002-10-24 09:35:37.452 barfoo[19606] 0x57cb0
Clearly, the Foundation/Core are playing games with the
NSArray/NSMutableArray class clusters. The +alloc methods return
placeholders that are turned into the appropriate instance upon
initialization. This makes sense given that the -init* method used
determines the role and, hence, the appropriate private subclass, of
the instance of the class cluster.
Now, consider the following:
[bumbox:~/bbum-developer/sourceforge/pyobjc] bbum% python
>>> from Foundation import *
>>> NSArray.alloc().init()
()
>>> NSMutableArray.alloc().init()
()
>>> NSArray.alloc().init()
2002-10-24 09:37:46.189 python[19612] Did you forget to nest alloc and
init?
2002-10-24 09:37:46.191 python[19612] *** Uncaught exception:
<NSInvalidArgumentException> *** -length only defined for abstract
class. Define -[NSPlaceholderMutableString length]!
It appears that the bridge somehow caches the placeholder instance and
ends up releasing or corrupting it upon the second call to the alloc()
method *for that particular class*?
Given that last bug, it seems like the shared placeholder array
instance may be being released one time to many??
It is! (see above)
|
|
From: Ronald O. <ous...@ci...> - 2002-10-24 06:30:09
|
The message is not very usefull, but there is a problem with your code. The following version does work: class HelloWorld (Foundation.NSObject): def sayIt(self): self.text = "Hello world" Note the 'self' argument for sayIt. You have to specify a self argument for all method definitions. Ronald On Thursday, Oct 24, 2002, at 07:52 Europe/Amsterdam, Steven D. Arnold wrote: > When trying to do a simple program, I discovered the following error, > which > I entered line for line in the python interpreter. > >>>> import sys >>>> import os.path >>>> sys.path.insert(0, os.p>>> >>> >>> ath.join(sys.path[0], "pyobjc")) >>>> import objc >>>> import Foundation >>>> import AppKit >>>> class HelloWorld(Foundation.NSObject): > ... def sayIt(): > ... self.text = "Hello World" > ... > No IMP for signature @@ > Traceback (most recent call last): > File "<stdin>", line 1, in ? > objc.error: No forwarder for signature @@ > > I tracked this down to the quoted text being the problem. If the > sayIt() > function doesn't include quoted text (either single- or > double-quotes), the > program doesn't raise an exception like this. > > I am using the CVS version of the software, downloaded a few days ago. > -- > > > > ------------------------------------------------------- > This sf.net email is sponsored by: Influence the future > of Java(TM) technology. Join the Java Community > Process(SM) (JCP(SM)) program now. > http://ad.doubleclick.net/clk;4729346;7592162;s?http://www.sun.com/ > javavote > _______________________________________________ > Pyobjc-dev mailing list > Pyo...@li... > https://lists.sourceforge.net/lists/listinfo/pyobjc-dev > |
|
From: Steven D. A. <st...@ne...> - 2002-10-24 05:52:46
|
When trying to do a simple program, I discovered the following error, which I entered line for line in the python interpreter. >>> import sys >>> import os.path >>> sys.path.insert(0, os.p>>> >>> >>> ath.join(sys.path[0], "pyobjc")) >>> import objc >>> import Foundation >>> import AppKit >>> class HelloWorld(Foundation.NSObject): ... def sayIt(): ... self.text = "Hello World" ... No IMP for signature @@ Traceback (most recent call last): File "<stdin>", line 1, in ? objc.error: No forwarder for signature @@ I tracked this down to the quoted text being the problem. If the sayIt() function doesn't include quoted text (either single- or double-quotes), the program doesn't raise an exception like this. I am using the CVS version of the software, downloaded a few days ago. -- |
|
From: Bill B. <bb...@co...> - 2002-10-24 03:52:51
|
If I change this...
class TestNSArrayInteraction( unittest.TestCase ):
def testNSArrayAllocation( self ):
NSCountFrames()
for i in range(1,1000):
print "c: %s" % i
a = NSArray.array()
b = NSArray.alloc().init()
... to this ...
class TestNSArrayInteraction( unittest.TestCase ):
def testNSArrayAllocation( self ):
NSCountFrames()
for i in range(1,1000):
print "c: %s" % i
a = NSArray.array()
b = NSArray.array()
... the unit test passes! The first test crashes during the second
pass through the loop and, once crashed, the stack is trashed.
Changing the test to...
class TestNSArrayInteraction( unittest.TestCase ):
def testNSArrayAllocation( self ):
NSCountFrames()
for i in range(1,1000):
print "c: %s pre alloc()" % i
a = NSArray.alloc()
print "c: %s pre init()" % i
a.init()
... spews this output:
[bumbox:~/bbum-developer/sourceforge/pyobjc] bbum% python
Lib/Foundation/test/test_nsobject.py
c: 1 pre alloc()
c: 1 pre init()
c: 2 pre alloc()
c: 2 pre init()
Segmentation fault
So -- it is during the second time that the bridge attempts to invoke
-init that it crashes. Specifically, when the NSInvocation object's
-invoke method is fired.
(I'm debugging "out loud" now -- hoping that someone will chime in w/an
"oh, yeah, that's easy!" :-)
b.bum
|
|
From: Bill B. <bb...@co...> - 2002-10-24 03:48:07
|
I'm trying to track down the segfault that occurs after the third or
fourth NSArray/NSDictionary is created. Bad bug. Trashes the stack.
Very hard to figure out (at least for me).
Some random tips...
- You can run the tests under gdb in project builder.... this gives
access to the handy dandy PBX debugging GUI.
Create a new foundation tool project
Delete the source and frameworks... everything but the product.
Add a custom executable. Set the executable to /usr/bin/python.
Add an argument. Set it to the path to one of the unit testing
scripts (or whatever).
Click the debug icon.....
That's it!
Now, to break somewhere in the bridge, you probably don't want to set
any old breakpoint. More likely, you want to set the breakpoints that
you want, then disable them, then set a single breakpoint that will be
hit after module initialization. To do this, I chose some random
debugging function that is relatively innocuous that I could call from
the python code.
Specifically....
class TestNSArrayInteraction( unittest.TestCase ):
def testNSArrayAllocation( self ):
NSCountFrames()
for i in range(1,1000):
print "c: %s" % i
a = NSArray.array()
b = NSArray.alloc().init()
.... I chose NSCountFrames(). In gdb, I set a breakpoint on
NSCountFrames(). When the breakpoint is hit, simply enable the real
breakpoints and continue execution.
Works for me, anyway.
|
|
From: <bb...@ma...> - 2002-10-24 01:24:11
|
If you grab the Project Builder template from the CVS repository (or my installer package-- though out of date, this part works) and create a new "Cocoa-Python Project", the resulting project will launch and run a simple application that has a button that, when clicked, fills a text field full of information. As such, it demonstrates both how to write action methods and how to create IBOutlets from which a connection can be made to a UI object in IB. As Ronald indicated, you have to manually create the subclass of NSObject (or whatever) in IB. Unfortunately, we don't currently have a "create source files" that'll create Python source from within IB. Well, maybe not true -- I saw in the repository that Ronald has another bit of magic in the works. :-) b.bum |
|
From: Ronald O. <ous...@ci...> - 2002-10-23 21:18:29
|
Steve, Using Interface Builder is easy enough. Like you wrote you can subclass NSObject (or any other class like NSView) in IB. You then define a subclass the same class in Python and that is all that is needed to automaticly use your class when the NIB is loaded. The easiest example for this is probably the TableModel2 example. Ronald |
|
From: Bill B. <bb...@co...> - 2002-10-23 21:13:43
|
BTW: Is there a way to express a dependency between the *.inc files and
their respective .m file? I ask because I have often updated the .inc,
forgotten to touch the .m, done a build and scratched my head for a sec
until I realize that the .m didn't recompile...
Excellent! Of the unit tests we have so far, only one is failing now
(nothing new):
......E.
======================================================================
ERROR: testClassInvocation (__main__.TestMethodInvocation)
----------------------------------------------------------------------
Traceback (most recent call last):
File "Lib/objc/test/test_objc.py", line 44, in testClassInvocation
self.assert_( objc.runtime.NSObject.description(), "Failed to
invoke the +description method." )
TypeError: Missing self argument
Very cool.
Ahh.... cocoa_generator.py -- not func_builder.py. That was why my
.inc files were tiny in comparison to the version in the repository.
I'm going to try to work through testing of the collection bridging as
that is a fairly major feature that could be the source of problems.
b.bum
On Wednesday, October 23, 2002, at 04:50 PM, Ronald Oussoren wrote:
> Corrrect. If fixed that script and reran 'scripts/cocoa_generator.py'.
> Afterwards I fixed a second problem: The functions were not marked as
> functions that accept keyword arguments, causing yet another bus > error.
|
|
From: Steven D. A. <st...@ne...> - 2002-10-23 20:57:43
|
Hi, Thanks for the work on a very cool project. I'm just learning Objective-C and PB/IB, so forgive me if I'm missing something obvious -- but is it possible to use pyobjc with Interface Builder? In IB, you'd subclass NSObject, add the actions and outlets you wanted, instantiate an object from the class, and wire it to your form. Then I suppose you'd write a class in Python that acted upon the items in the form. But how do you get the NIB to understand that it's supposed to use the Python class that I've provided? steve -- |
|
From: Ronald O. <ous...@ci...> - 2002-10-23 20:50:07
|
On Wednesday, Oct 23, 2002, at 16:57 Europe/Amsterdam, Bill Bumgarner wrote: > I added a test_paths.py suite to Foundation to test the > NSPathUtilities APIs. > > The NSSearchPathForDirectoriesInDomains() function is causing a bus > error, at the moment: [...] > hat the auto-generated wrappers are not invoking > PyArg_ParseTupleAndKeywords() correctly in that the list of variables > to parse the args into are passed by value, not by reference. For > example... > > if (PyArg_ParseTupleAndKeywords(args, kwds, > "iii:NSSearchPathForDirectoriesInDomains", keywords, objc_directory, > objc_domainMask, objc_expandTilde) < 0) return NULL; > > ... the three objc_* should all be prefixed by an &, correct? Corrrect. If fixed that script and reran 'scripts/cocoa_generator.py'. Afterwards I fixed a second problem: The functions were not marked as functions that accept keyword arguments, causing yet another bus error. Ronald |
|
From: Bill B. <bb...@co...> - 2002-10-23 16:26:32
|
On Wednesday, October 23, 2002, at 12:00 PM, pyt...@py... wrote: > First off, the PyObjC bridge clean up work that was done by bbum and > Ronald is truly truly welcome. I've been eyeing the pyobjc SF project > wishing it was up to date for 6 months and wishing I knew more about > the guts of ObjC to get into it. Its cleanup release was just about as > timely as it could be. Thank you. We are currently integrating a set of unit tests that validate the bridge and, from that, fixing any major/glaring bugs in preparation for pushing out a 0.7.1 release. Once that is out the door, we are looking to clean up everything to move towards a 1.0alpha stage. I have been using the bridge for a while now to build a production quality Cocoa application that provides a GUI for a relatively complex web services enabled application (www.codefab.com has more information). It works really well, though there are some issues that need to be ironed out. At this point, I can confidently say that both my productive and the quality of the product have been improved by the introduction of PyObjC into the mix. > Its been said before (even on this list) but bringing python to cocoa > is a great idea if only to get more developers like me who look at objc > syntax and think of chinese algebra. Apple would do well to pick this > up and make it part of standard OSX. Please file an enhancement request via bugreport.apple.com asking Apple to integrate PyObjC into the release of Python included with OS X. Mention that a Project Builder Template exists and enables first class Cocoa development in Python and feel free to give them my email address. The more requests they receive, the more likely it is to happen. I'm quite willing to lose some sleep making sure it happens correctly, if Apple decides to "go there". > .... notes on performance realities deleted .... > So here is my question to those of you with more bridge experience: > > Can the performance penalty (if one exists) be quantified > order-of-magnitude for those of us that are curious? Perhaps even by > category, i.e, startup time, memory management, event handling, etc. > This could also help those of us looking to get involved in identifying > where to dig into ObjC/Cocoa/pyobjc... First, the launch times of PyObjC based applications is currently very slow because Apple does not ship a complete Python development kit with 10.2. Namely, the Python supplied by Apple is missing a library and dylib that can be used to embed the Python interpreter into an application. As such, both the Foundation and the AppKit frameworks (and all other frameworks that come along with them) have to be dynamically loaded via NSBundle's API. This causes a huge performance hit when starting up an application. In using the Fink build of Python, I did some experiments where I embedded the Python interpreter into the application and did not "bootstrap" out to the /usr/bin/python interpreter. Launch times were significantly faster, but still not what they should be. As it stands, the pyobjc module behaves suboptimally when imported into Python in that it ends up traversing the entire Obj-C runtime as it binds itself into the interpreter. There is a lot of opportunity for optimization within this, but it isn't going to happen before 1.0. I haven't quantified the cost of messaging between the two environments against, say, a regular Python->Python or ObjC->ObjC method invocation. In general, the [potential, but maybe not as currently implemented] cost of crossing the bridge under PyObjC is significantly less than the cost of crossing the bridge under the Java bridge. In particular, Python and Objective-C are both dynamic and light weight in nature. As such, it is fairly straightforward to bridge objects between the two. For example, Ronald created a subclass of NSMutableDictionary that wraps around a PyDict object-- a python dictionary-- such that a dict passed across to the Obj-C side of the bridge doesn't have to be converted to the native ObjC collection class (for the Java bridge, the developer frequently ends up having to do these kinds of conversions because there is no way to do this kind of bridging). In general, performance of a Cocoa application is more a matter of appropriately gluing the objects together and less about how fast you can implement a particular algorithm. As such and regardless of language chosen, a lot of performance gains can typically be had by, say, implementing a better updating/invalidation alogrithm or improving data caching. One of the key advantages of PyObjC is the ability to easily port a performance critical piece of code from Python to ObjC. Do your prototyping and development in Python.... if something proves to be a performance bottleneck, see if you can't optimize the architecture to improve the situation (this goes much faster with Python than ObjC).... once the architecture is fairly optimal, port to ObjC if you need more performance. > Thanks. Again, getting PyObjc fixed up for 10.2 has been like an early > Christmas present this year. Glad you like it! > Antonio b.bum |
|
From: Bill B. <bb...@co...> - 2002-10-23 14:58:00
|
I added a test_paths.py suite to Foundation to test the NSPathUtilities
APIs.
The NSSearchPathForDirectoriesInDomains() function is causing a bus
error, at the moment:
class TestSearchPaths( unittest.TestCase ):
def testSearchPaths( self ):
self.assert_( NSSearchPathForDirectoriesInDomains(
NSAllLibrariesDirectory, NSAllDomainsMask, NO ),
"NSSearchPathForDirectoriesInDomains() failed to
return anything." )
It appears that the auto-generated wrappers are not invoking
PyArg_ParseTupleAndKeywords() correctly in that the list of variables
to parse the args into are passed by value, not by reference. For
example...
if (PyArg_ParseTupleAndKeywords(args, kwds,
"iii:NSSearchPathForDirectoriesInDomains", keywords, objc_directory,
objc_domainMask, objc_expandTilde) < 0) return NULL;
... the three objc_* should all be prefixed by an &, correct? I had a
look at scripts/func_* and wasn't entirely certain how to re-run the
scripts once modified.
Gdb session follows....
b.bum
---
[bumbox:~/bbum-developer/sourceforge/pyobjc] bbum% gdb python
GNU gdb 5.1-20020408 (Apple version gdb-231) (Tue Aug 13 21:37:39 GMT
2002)
....
Reading symbols for shared libraries .... done
(gdb) r Lib/Foundation/test/test_paths.py
Starting program: /usr/bin/python Lib/Foundation/test/test_paths.py
[Switching to process 17312 thread 0xb03]
...
Program received signal EXC_BAD_ACCESS, Could not access memory.
0x0000796c in PyArg_VaParse ()
(gdb) bt
#0 0x0000796c in PyArg_VaParse ()
#1 0x00007470 in PyArg_VaParse ()
#2 0x00008b60 in PyArg_ParseTupleAndKeywords ()
#3 0x0000872c in PyArg_ParseTupleAndKeywords ()
#4 0x0080f340 in objc_NSSearchPathForDirectoriesInDomains (self=0x0,
args=0x9d840, kwds=0xbfffde20) at Modules/Cocoa/_Fnd_Functions.inc:980
#5 0x00045930 in PyObject_Call ()
#6 0x0005df64 in PyEval_GetFuncDesc ()
#7 0x0005b1f8 in PyEval_EvalCode ()
#8 0x0005c634 in PyEval_EvalCodeEx ()
#9 0x0005db9c in PyEval_GetFuncDesc ()
#10 0x0005b314 in PyEval_EvalCode ()
#11 0x0005c634 in PyEval_EvalCodeEx ()
#12 0x0006d870 in PyFunction_SetClosure ()
#13 0x00045930 in PyObject_Call ()
#14 0x00056d74 in PyMethod_New ()
#15 0x00045930 in PyObject_Call ()
#16 0x000562cc in PyInstance_New ()
#17 0x00045930 in PyObject_Call ()
#18 0x0005df64 in PyEval_GetFuncDesc ()
#19 0x0005b32c in PyEval_EvalCode ()
#20 0x0005c634 in PyEval_EvalCodeEx ()
#21 0x0006d870 in PyFunction_SetClosure ()
#22 0x00045930 in PyObject_Call ()
#23 0x00056d74 in PyMethod_New ()
#24 0x00045930 in PyObject_Call ()
#25 0x000562cc in PyInstance_New ()
#26 0x00045930 in PyObject_Call ()
#27 0x0005df64 in PyEval_GetFuncDesc ()
#28 0x0005b32c in PyEval_EvalCode ()
#29 0x0005c634 in PyEval_EvalCodeEx ()
#30 0x0006d870 in PyFunction_SetClosure ()
#31 0x00045930 in PyObject_Call ()
#32 0x00056d74 in PyMethod_New ()
#33 0x00045930 in PyObject_Call ()
#34 0x000562cc in PyInstance_New ()
#35 0x00045930 in PyObject_Call ()
#36 0x0005df64 in PyEval_GetFuncDesc ()
#37 0x0005b32c in PyEval_EvalCode ()
#38 0x0005c634 in PyEval_EvalCodeEx ()
#39 0x0005db9c in PyEval_GetFuncDesc ()
#40 0x0005b314 in PyEval_EvalCode ()
#41 0x0005c634 in PyEval_EvalCodeEx ()
#42 0x0005db9c in PyEval_GetFuncDesc ()
#43 0x0005b314 in PyEval_EvalCode ()
#44 0x0005c634 in PyEval_EvalCodeEx ()
#45 0x0006d870 in PyFunction_SetClosure ()
#46 0x00045930 in PyObject_Call ()
#47 0x00056d74 in PyMethod_New ()
#48 0x00045930 in PyObject_Call ()
#49 0x0005d838 in PyEval_CallObjectWithKeywords ()
#50 0x000527c0 in PyInstance_New ()
#51 0x00045930 in PyObject_Call ()
#52 0x0005df64 in PyEval_GetFuncDesc ()
#53 0x0005b32c in PyEval_EvalCode ()
#54 0x0005c634 in PyEval_EvalCodeEx ()
#55 0x00058a80 in PyEval_EvalCode ()
#56 0x00027e90 in PyRun_FileExFlags ()
#57 0x00026ef4 in PyRun_SimpleFileExFlags ()
#58 0x000069f0 in Py_Main ()
#59 0x00002970 in start ()
#60 0x000027f0 in start ()
|
|
From: Bill B. <bb...@co...> - 2002-10-22 22:12:56
|
I cleaned up the organization of the objc test cases... I also really committed the silly main-bin-python.m file. Sorry. b.bum |
|
From: Bill B. <bb...@co...> - 2002-10-22 21:23:23
|
On Tuesday, October 22, 2002, at 05:03 PM, Ronald Oussoren wrote:
> On Tuesday, Oct 22, 2002, at 22:47 Europe/Amsterdam, Bill Bumgarner
> wrote:
> .....
>>
>> I'm pretty happy with the latest incarnation of bin-python-main.m --
>> need to commit a new version and update the python project template
>> -- as it has proven to not be problematic in the production app I'm
>> working on. It now supports standalone builds with frameworks
>> (dynamically loading frameworks with @executable_path as their
>> install_name AND dependencies on other frameworks w/@executable_path
>> requires some fun to be had with the environment variables of the
>> execve() process).
> I'll also have a more serious look bin-python-main.m. I definitely
> like the idea of just exec-ing a normal python with the right argv.
>
> The only thing I'm not sure about w.r.t. binary distribution of
> applications is honouring a 'defaults' settings for the location of
> the python binary. But that is something we can fix in our in-house
> version of python (we ship extension modules, and those might not work
> with a different version of python).
It already does that and much, much more! :-)
See both the main-bin-python.m and the Main.py in the Web Services Tool
example. It is the "most correct" version I have created so far and
will be copied to the other projects / locations soon. Some of the
nifty features:
- auto dumps the pid for attaching to the child python process via
gdb if SHOWPID is set in the environment
- correctly sets up environment variables to deal w/linking
frameworks in production vs. in project builder
- traverses list of frameworks linked into the main-bin-python.m
binary and passes 'em off to the Main.py. Main.py dynamically loads
each framework, then checks each framework to see if it has an Init.py
script in Resources/ and executes it, if it does. I.e. frameworks will
auto-bootstrap their python stuff as they are loaded into the "real"
environment.
- can customize which python binary is used via the PythonBinPath
user default
- can customize the Main script used via the main bundles
PrincipalPythonFile dictionary entry; defaults to Main.py (should add
this to the project template, now that I think about it).
- everything is done w/UTF-8 encoded strings and, as such, should
work on any OS X installation [as the BSD layer all deals w/UTF-8
encoded strings for paths and such].
- setting the DYLD_PRINT_LIBRARIES user default to 1 will cause the
child python process to dump all of the dylibs as they are loaded
>> I'll try to re-address the documentation on the examples that use
>> Project Builder and put together a README for the Project Template
>> stuff.
> Documentation, the mortal enemy of coders :-)
I like using unit tests as documentation, personally. We [CodeFab]
have been using XP in varying levels of intensity over the past few
years. When it works, it works really well.
b.bum
|
|
From: Ronald O. <ous...@ci...> - 2002-10-22 21:03:01
|
On Tuesday, Oct 22, 2002, at 22:47 Europe/Amsterdam, Bill Bumgarner wrote: > How about a feature freeze -- fix the bugs we have right now that are > relatively major [dictionary / array allocation, the instance > identification] and add unit tests to ensure that whatever we ship as > 0.7.0 (or whatever) is relatively solid? I feature freeze is fine with me, I don't have any new version planned for the forseeable future :-) I'll have a look at the remaining know bugs. BTW. I increased the version number in CVS to avoid confusion with the installer on your home-page. > > I'm pretty happy with the latest incarnation of bin-python-main.m -- > need to commit a new version and update the python project template -- > as it has proven to not be problematic in the production app I'm > working on. It now supports standalone builds with frameworks > (dynamically loading frameworks with @executable_path as their > install_name AND dependencies on other frameworks w/@executable_path > requires some fun to be had with the environment variables of the > execve() process). I'll also have a more serious look bin-python-main.m. I definitely like the idea of just exec-ing a normal python with the right argv. The only thing I'm not sure about w.r.t. binary distribution of applications is honouring a 'defaults' settings for the location of the python binary. But that is something we can fix in our in-house version of python (we ship extension modules, and those might not work with a different version of python). > > I'll try to re-address the documentation on the examples that use > Project Builder and put together a README for the Project Template > stuff. Documentation, the mortal enemy of coders :-) Ronald |
|
From: Bill B. <bb...@co...> - 2002-10-22 20:47:16
|
Agreed. The differences between 0.6.1 and 0.7.0 are huge and the visibility for the module within the dev community is going to grow quite a bit in the near future [I hope -- will say more when I know more]. How about a feature freeze -- fix the bugs we have right now that are relatively major [dictionary / array allocation, the instance identification] and add unit tests to ensure that whatever we ship as 0.7.0 (or whatever) is relatively solid? I'm pretty happy with the latest incarnation of bin-python-main.m -- need to commit a new version and update the python project template -- as it has proven to not be problematic in the production app I'm working on. It now supports standalone builds with frameworks (dynamically loading frameworks with @executable_path as their install_name AND dependencies on other frameworks w/@executable_path requires some fun to be had with the environment variables of the execve() process). I'll try to re-address the documentation on the examples that use Project Builder and put together a README for the Project Template stuff. b.bum On Tuesday, October 22, 2002, at 04:40 PM, Ronald Oussoren wrote: >> [This is not a request to do a 1.0 release without an official >> interim release -- just some planning for the real live 1.0] > Speaking of interim releases... Now that people are getting > interrested in PyObjC it would be better to get one out sometime soon. |
|
From: Ronald O. <ous...@ci...> - 2002-10-22 20:40:08
|
On Tuesday, Oct 22, 2002, at 07:25 Europe/Amsterdam, Bill Bumgarner wrote: > [This is not a request to do a 1.0 release without an official interim > release -- just some planning for the real live 1.0] Speaking of interim releases... Now that people are getting interrested in PyObjC it would be better to get one out sometime soon. > > In the interests in achieving a 1.0 release, I thought I would take a > very brief moment to toss out some ideas of what the project needs to > get there. > > --- > > First and foremost, we need a unit testing suite that provides > complete coverage for the modules features. As it stands, I keep > stepping on Ronald's toes and he keeps stepping on mine -- once we > work through the issues, the end result tends to work everywhere, but > the interim is a pain for both of us. This is largely because Ronald > and I work against different builds of python [he uses a framework > build of 2.3 alpha and I use the Apple supplied-- slightly > incomplete-- build of 2.2] and work with a different set of examples > largely to provide testing. I try to test new features using 'Apple Python', but my primary Python environment is Python 2.3alpha. Having a unit testing suite would indeed be very usefull. I'd like to use PyUnit (which IIRC is what is used by the Python testsuite). > Documentation: Documentation is a very much needed. There is some documentation, but it needs to be fleshed out. Luckily we don't need a lot of it :-) > > --- > > Examples: > > - need a general clean up and synchronization with underlying > features (example: web services tool currently does a bunch > additional selector() definitions that are likely completely > unnecessary) > > - remove examples that no longer work correctly or are rendered > moot by unit testing <nod> Most of the current examples are actually scripts that I used to test the code. > --- > > Remove all references to True and False throughout codebase > [assuming we are to continue supporting 2.2 throughout] -- or declare > 'em somewhere. I'd prefer to declare True and False on Python 2.2.0. Although the values don't really add functionality they do help with readability. Ronald |