Discussion:
[Pyobjc-dev] [ pyobjc-Bugs-836247 ] NSWindow.contentRectForFrameRect_styleMask_ not a class meth
SourceForge.net
2003-11-05 01:57:25 UTC
Permalink
Bugs item #836247, was opened at 2003-11-05 03:56
Message generated for change (Tracker Item Submitted) made by Item Submitter
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=114534&aid=836247&group_id=14534

Category: None
Group: None
Status: Open
Resolution: None
Priority: 5
Submitted By: Zachery Bir (zbir)
Assigned to: Nobody/Anonymous (nobody)
Summary: NSWindow.contentRectForFrameRect_styleMask_ not a class meth

Initial Comment:
Trying the following snippet of code:

def showPrefPane(self, pane, sender):
windowFrame =
NSWindow.contentRectForFrameRect_styleMask_(
NSWindow,
self.window().frame(),
self.window().styleMask())
newWindowHeight = NSHeight(pane.frame())
if self.window().toolbar().isVisible():
newWindowHeight += NSHeight(

self.window().toolbar()._toolbarView().frame())
newWindowFrame =
NSWindow.frameRectForContentRect_styleMask_(
NSWindow,
NSMakeRect(NSMinX(windowFrame),
NSMaxY(windowFrame) -
newWindowHeight,
NSWidth(windowFrame),
newWindowHeight),
self.window().styleMask())
self.window().setContentView_(pane)

self.window().setFrame_display_animate_(newWindowFra
me,
YES,

self.window().isVisible())

**********

Host: gorilla.urbanape.com
Date/Time: 2003-11-04 22:52:17 -0500
OS Version: 10.3 (Build 7B85)

Command: python (/Users/zbir/Applications/
ZopeEditManager.app/Contents/MacOS/python)
PID: 613
Thread: 0

Exception: EXC_BAD_ACCESS (0x0001)
Codes: KERN_PROTECTION_FAILURE (0x0002) at
0x0000000c

Thread 0 Crashed:
#0 0x90831388 in objc_msgSend_stret
(objc_msgSend_stret + 8)
#1 0x92dcff08 in -[NSWindow contentRectForFrameRect:
styleMask:] (-[NSWindow contentRectForFrameRect:
styleMask:] + 160)
#2 0x00428974 in ffi_call_DARWIN (ffi_call_DARWIN +
208)
#3 0x00428368 in ffi_call (ffi_darwin.c:401)
#4 0x00426f08 in ObjC_FFICaller (libffi_support.m:1088)
#5 0x0041a12c in objcsel_call (selector.m:594)
#6 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#7 0x95fa9ba8 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 2268)
#8 0x95fa9598 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 716)
#9 0x95fa6c64 in PyEval_EvalCode (PyEval_EvalCode +
9568)
#10 0x95fa7e30 in PyEval_EvalCodeEx
(PyEval_EvalCodeEx + 2128)
#11 0x95f5f354 in PyFunction_SetClosure
(PyFunction_SetClosure + 3436)
#12 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#13 0x0041b13c in pysel_call (selector.m:997)
#14 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#15 0x95fa9ba8 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 2268)
#16 0x95fa9598 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 716)
#17 0x95fa6c64 in PyEval_EvalCode (PyEval_EvalCode +
9568)
#18 0x95fa7e30 in PyEval_EvalCodeEx
(PyEval_EvalCodeEx + 2128)
#19 0x95f5f354 in PyFunction_SetClosure
(PyFunction_SetClosure + 3436)
#20 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#21 0x0041b13c in pysel_call (selector.m:997)
#22 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#23 0x95fa9ba8 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 2268)
#24 0x95fa9598 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 716)
#25 0x95fa6c64 in PyEval_EvalCode (PyEval_EvalCode +
9568)
#26 0x95fa7e30 in PyEval_EvalCodeEx
(PyEval_EvalCodeEx + 2128)
#27 0x95f5f354 in PyFunction_SetClosure
(PyFunction_SetClosure + 3436)
#28 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#29 0x0041b024 in pysel_call (selector.m:979)
#30 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#31 0x004104ac in PyObjC_CallPython (class-builder.m:
1410)
#32 0x0042524c in method_stub (libffi_support.m:416)
#33 0x004286ac in ffi_closure_helper_DARWIN
(ffi_darwin.c:699)
#34 0x00428a44 in ffi_closure_ASM (ffi_closure_ASM +
116)
#35 0x92df240c in -[NSIBObjectData
nibInstantiateWithOwner:topLevelObjects:] (-
[NSIBObjectData nibInstantiateWithOwner:
topLevelObjects:] + 920)
#36 0x92ee3ab4 in loadNib (loadNib + 252)
#37 0x92e3ae64 in +[NSBundle(NSNibLoading)
_loadNibFile:nameTable:withZone:ownerBundle:]
(+[NSBundle(NSNibLoading) _loadNibFile:nameTable:
withZone:ownerBundle:] + 744)
#38 0x92eb9b58 in +[NSBundle(NSNibLoading)
loadNibFile:externalNameTable:withZone:]
(+[NSBundle(NSNibLoading) loadNibFile:
externalNameTable:withZone:] + 156)
#39 0x92ec095c in -[NSWindowController loadWindow] (-
[NSWindowController loadWindow] + 204)
#40 0x92e75128 in -[NSWindowController window] (-
[NSWindowController window] + 92)
#41 0x92f31adc in -[NSWindowController showWindow:]
(-[NSWindowController showWindow:] + 36)
#42 0x00428974 in ffi_call_DARWIN (ffi_call_DARWIN +
208)
#43 0x00428368 in ffi_call (ffi_darwin.c:401)
#44 0x00426f2c in ObjC_FFICaller (libffi_support.m:
1097)
#45 0x0041a010 in objcsel_call (selector.m:575)
#46 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#47 0x95fa9ba8 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 2268)
#48 0x95fa9598 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 716)
#49 0x95fa6c64 in PyEval_EvalCode (PyEval_EvalCode +
9568)
#50 0x95fa7e30 in PyEval_EvalCodeEx
(PyEval_EvalCodeEx + 2128)
#51 0x95f5f354 in PyFunction_SetClosure
(PyFunction_SetClosure + 3436)
#52 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#53 0x0041b024 in pysel_call (selector.m:979)
#54 0x95f4a8d0 in PyObject_Call (PyObject_Call + 48)
#55 0x004104ac in PyObjC_CallPython (class-builder.m:
1410)
#56 0x0042524c in method_stub (libffi_support.m:416)
#57 0x004286ac in ffi_closure_helper_DARWIN
(ffi_darwin.c:699)
#58 0x00428a44 in ffi_closure_ASM (ffi_closure_ASM +
116)
#59 0x92e779d0 in -[NSApplication sendAction:to:from:]
(-[NSApplication sendAction:to:from:] + 108)
#60 0x92ead1bc in -[NSMenu
performActionForItemAtIndex:] (-[NSMenu
performActionForItemAtIndex:] + 392)
#61 0x92ef1ac4 in -[NSCarbonMenuImpl
performActionWithHighlightingForItemAtIndex:] (-
[NSCarbonMenuImpl
performActionWithHighlightingForItemAtIndex:] + 104)
#62 0x92ef83f4 in -[NSMenu performKeyEquivalent:] (-
[NSMenu performKeyEquivalent:] + 260)
#63 0x92ed7420 in -[NSApplication
_handleKeyEquivalent:] (-[NSApplication
_handleKeyEquivalent:] + 292)
#64 0x92df4eec in -[NSApplication sendEvent:] (-
[NSApplication sendEvent:] + 2652)
#65 0x92dfd754 in -[NSApplication run] (-[NSApplication
run] + 576)
#66 0x92eb9a1c in NSApplicationMain (NSApplicationMain
+ 464)
#67 0x006beda8 in objc_NSApplicationMain (_AppKit.m:
116)
#68 0x95fa94a8 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 476)
#69 0x95fa6c64 in PyEval_EvalCode (PyEval_EvalCode +
9568)
#70 0x95fa7e30 in PyEval_EvalCodeEx
(PyEval_EvalCodeEx + 2128)
#71 0x95fa97dc in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 1296)
#72 0x95fa9580 in PyEval_GetFuncDesc
(PyEval_GetFuncDesc + 692)
#73 0x95fa6c64 in PyEval_EvalCode (PyEval_EvalCode +
9568)
#74 0x95fa7e30 in PyEval_EvalCodeEx
(PyEval_EvalCodeEx + 2128)
#75 0x95fa4734 in PyEval_EvalCode (PyEval_EvalCode +
48)
#76 0x95fc85f0 in PyRun_FileExFlags (PyRun_FileExFlags
+ 228)
#77 0x95fc7668 in PyRun_SimpleFileExFlags
(PyRun_SimpleFileExFlags + 444)
#78 0x95fd1ec0 in Py_Main (Py_Main + 1996)
#79 0x00003c78 in start (start + 444)
#80 0x00003aec in start (start + 48)

PPC Thread State:
srr0: 0x90831388 srr1: 0x0200f030 vrsave:
0x00000000
cr: 0x40224242 xer: 0x20000004 lr: 0x92dcff08 ctr:
0x90831380
r0: 0x92dcff08 r1: 0xbfffa150 r2: 0xa2dc1535 r3:
0xbfffa190
r4: 0x0000000c r5: 0x9086ee4c r6: 0x42200000 r7:
0x443cc000
r8: 0x43c80000 r9: 0x42a60000 r10: 0x00000003
r11: 0xa2dc1fb4
r12: 0x90831380 r13: 0x0123d060 r14: 0x00000000
r15: 0x00791558
r16: 0x00000000 r17: 0x00000000 r18: 0x00000000
r19: 0x00000000
r20: 0x00000000 r21: 0x003054c0 r22: 0x00000003
r23: 0x0037412e
r24: 0xbfffaf2c r25: 0x00366f84 r26: 0x00000000 r27:
0x01148920
r28: 0x00000003 r29: 0xa2dfce3c r30: 0xbfffa1e4 r31:
0x92dcfe68

----------------------------------------------------------------------

You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=114534&aid=836247&group_id=14534
Bob Ippolito
2003-11-05 06:25:03 UTC
Permalink
Post by SourceForge.net
Bugs item #836247, was opened at 2003-11-05 03:56
Message generated for change (Tracker Item Submitted) made by Item Submitter
https://sourceforge.net/tracker/?
func=detail&atid=114534&aid=836247&group_id=14534
This bug exposes a fairly obnoxious problem in the current
implementation of PyObjC: there are not separate namespaces for class
and instance methods, so you end up with stupid problems when you have
some class that implements the same selector for both the instance and
class. There's a lot of pretty common selectors that are like this:
+[NSObject description]
-[NSObject description]
.. etc ..

I did discover a workaround, which is
Post by SourceForge.net
NSObject.description(NSObject)
u'<NSObject: 0xa0a04e40>'

Ideally one would be able to do NSObject.description().. which means
that the descriptor has to know about these "class or instance"
methods. It's also, as far as I know, not possible to override the
class implementation for a selector if an instance implementation
exists (from Python).

So my question is, how the heck do we approach this? It seems that the
current selector objects/descriptors need to be changed quite a bit in
order to facilitate this, especially allowing one to do it from Python.
Perhaps we can change the selector function to take a class function,
instance function, or both.. and throw away isClassMethod. We can add
a flag on the selector that says "I have both a class and an instance".
I'm pretty new to these internals.. so, Ronald, do you want to handle
this one? Or give me some ideas as to how this should be done?

-bob
Bob Ippolito
2003-11-05 07:43:04 UTC
Permalink
Post by Bob Ippolito
Post by SourceForge.net
Bugs item #836247, was opened at 2003-11-05 03:56
Message generated for change (Tracker Item Submitted) made by Item Submitter
https://sourceforge.net/tracker/?
func=detail&atid=114534&aid=836247&group_id=14534
This bug exposes a fairly obnoxious problem in the current
implementation of PyObjC: there are not separate namespaces for class
and instance methods, so you end up with stupid problems when you have
some class that implements the same selector for both the instance and
+[NSObject description]
-[NSObject description]
.. etc ..
I did discover a workaround, which is
Post by SourceForge.net
NSObject.description(NSObject)
u'<NSObject: 0xa0a04e40>'
Ideally one would be able to do NSObject.description().. which means
that the descriptor has to know about these "class or instance"
methods. It's also, as far as I know, not possible to override the
class implementation for a selector if an instance implementation
exists (from Python).
So my question is, how the heck do we approach this? It seems that
the current selector objects/descriptors need to be changed quite a
bit in order to facilitate this, especially allowing one to do it from
Python. Perhaps we can change the selector function to take a class
function, instance function, or both.. and throw away isClassMethod.
We can add a flag on the selector that says "I have both a class and
an instance". I'm pretty new to these internals.. so, Ronald, do you
want to handle this one? Or give me some ideas as to how this should
be done?
Post by SourceForge.net
NSObject.pyobjc_classMethods.description()
u'NSObject'
Post by Bob Ippolito
Post by SourceForge.net
NSObject.alloc().init().pyobjc_instanceMethods.description()
u'<NSObject: 0x3a3c60>'

That is the real workaround.. fixed tests checked in.

-bob
Ronald Oussoren
2003-11-05 08:01:01 UTC
Permalink
Managment summary:

use 'NSWindow.pyobjc_classMethods.contentRectForFrameRect_styleMask_'
to call the class method.
Post by Bob Ippolito
Post by SourceForge.net
Bugs item #836247, was opened at 2003-11-05 03:56
Message generated for change (Tracker Item Submitted) made by Item Submitter
https://sourceforge.net/tracker/?
func=detail&atid=114534&aid=836247&group_id=14534
This bug exposes a fairly obnoxious problem in the current
implementation of PyObjC: there are not separate namespaces for class
and instance methods, so you end up with stupid problems when you have
some class that implements the same selector for both the instance and
+[NSObject description]
-[NSObject description]
.. etc ..
I noticed. It is already possible to call both methods, although not
necessarily in a very convenient way:

NSObject.foo() # Instancemethod, or if that does not exist classmethod
NSObject.pyobjc_classMethods.foo() # Classmethod
NSObject.pyobjc_instanceMethods.foo() # Instancemethod

At the time we ran into this issue I couldn't find any methods where it
would be usefull to override the class method, it is therefore not
possible to override the class method at the moment.
Post by Bob Ippolito
I did discover a workaround, which is
Post by SourceForge.net
NSObject.description(NSObject)
u'<NSObject: 0xa0a04e40>'
That's more a bug than a feature...
Post by Bob Ippolito
Ideally one would be able to do NSObject.description().. which means
that the descriptor has to know about these "class or instance"
methods. It's also, as far as I know, not possible to override the
class implementation for a selector if an instance implementation
exists (from Python).
Note that it is currently not possible to know of
'NSObject.description' refers to the class or instance method due to
the way types are implemented in Python:

obj.description()

is basically implemented as:

obj.__class__.description(obj)

And furthermore Cocoa classes should 'feel' as much as Python classes
as possible, it should therefore be possible to call
'NSObject.description(obj)'.
Post by Bob Ippolito
So my question is, how the heck do we approach this? It seems that
the current selector objects/descriptors need to be changed quite a
bit in order to facilitate this, especially allowing one to do it from
Python. Perhaps we can change the selector function to take a class
function, instance function, or both.. and throw away isClassMethod.
We can add a flag on the selector that says "I have both a class and
an instance". I'm pretty new to these internals.. so, Ronald, do you
want to handle this one? Or give me some ideas as to how this should
be done?
Is it necessary to be able to override class methods? My gut feeling is
that it would be easy-ish to change objc.selector() to allow something
like this:

class FooClass (NSObject):
def _cls_description(cls):
return "FooClass class"

def _inst_description(self):
return "FooClass instance"

description = objc.selector(selector="description",
clsImp=_cls_description, instImp=_inst_description)

del _cls_description, _inst_description

This is pretty ugly, but at least would make it possible to override
both the class and instance method without introducing an additional
class.

However, I don't think this is worth the effort unless someone has a
real use case.

Ronald
Bob Ippolito
2003-11-05 09:26:06 UTC
Permalink
Post by Bob Ippolito
Ideally one would be able to do NSObject.description().. which means
that the descriptor has to know about these "class or instance"
methods. It's also, as far as I know, not possible to override the
class implementation for a selector if an instance implementation
exists (from Python).
Note that it is currently not possible to know of
'NSObject.description' refers to the class or instance method due to
obj.description()
obj.__class__.description(obj)
And furthermore Cocoa classes should 'feel' as much as Python classes
as possible, it should therefore be possible to call
'NSObject.description(obj)'.
Well the current semantics for instance method only and class method
only are perfectly fine.. it's the occasions when a particular selector
is implemented both ways where things start to get hairy. I'd rather
have NSObject.description(obj) not work, if it meant that
NSObject.description() did work. Even still, I think enough
information is available to make it possible. What about something
like this (sorta pseudocode, not in tp_descr_get C API).

METH_CLASS = 1 << 0
METH_INST = 1 << 1

class Selector(object):
EXPECTED_ARG_LEN = 0
def __init__(self, name, flags, boundklass=None, boundinst=None):
self.name = name
self.flags = flags
self.boundklass = boundklass
self.boundinst = boundinst

def __get__(self, inst, klass):
if inst is None:
return Selector(self.name, self.flags, boundklass=klass)
elif not (self.flags & METH_INST):
# trying to get a pure class method from an instance, don't
do that
raise AttributeError, "%s is a class method" % (self.name,)
# it's been acquired from an instance
return Selector(self.name, self.flags, boundinst=inst,
boundklass=klass)

def __call__(self, *args):
if self.boundklass is None and self.boundinst is None:
raise ValueError, "Selector not initialized"
elif self.boundinst is not None:
# acquired from an instance..
return execute(self.boundinst, args)
elif self.flags & METH_INST and len(args) >
self.EXPECTED_ARG_LEN:
# must be using class.instanceMethod(instance, ....)
# so what if this breaks down on varargs? how are varargs
wrapped anyway (if at all)?
# those people can use a workaround or just not use
klass.instanceMethod(instance, ...)
return execute(args[0], args[1:])
else:
return execute(self.boundklass, args)
Bob Ippolito
2003-11-05 14:41:16 UTC
Permalink
Post by Ronald Oussoren
NSObject.foo() # Instancemethod, or if that does not exist
classmethod
NSObject.pyobjc_classMethods.foo() # Classmethod
NSObject.pyobjc_instanceMethods.foo() # Instancemethod
Another idea - how about putting that on the selector instead?

NSObject.foo() # smart decision by the selector (smarter than
instance if it has one, see previous email)
NSObject.foo.classMethod() # classmethod
NSObject.foo.instanceMethod() # instanceMethod

-bob
Jack Jansen
2003-11-05 20:25:23 UTC
Permalink
Post by Bob Ippolito
Post by Ronald Oussoren
NSObject.foo() # Instancemethod, or if that does not exist
classmethod
NSObject.pyobjc_classMethods.foo() # Classmethod
NSObject.pyobjc_instanceMethods.foo() # Instancemethod
Another idea - how about putting that on the selector instead?
NSObject.foo() # smart decision by the selector (smarter than
instance if it has one, see previous email)
NSObject.foo.classMethod() # classmethod
NSObject.foo.instanceMethod() # instanceMethod
That would make "NSObject.foo" a funny sort of object. In the current
scheme NSObject.foo is a perfectly normal object, either an unbound
method or a class method.
--
Jack Jansen, <***@cwi.nl>, http://www.cwi.nl/~jack
If I can't dance I don't want to be part of your revolution -- Emma
Goldman
Bob Ippolito
2003-11-05 20:45:10 UTC
Permalink
Post by Jack Jansen
Post by Bob Ippolito
Post by Ronald Oussoren
NSObject.foo() # Instancemethod, or if that does not exist
classmethod
NSObject.pyobjc_classMethods.foo() # Classmethod
NSObject.pyobjc_instanceMethods.foo() # Instancemethod
Another idea - how about putting that on the selector instead?
NSObject.foo() # smart decision by the selector (smarter than
instance if it has one, see previous email)
NSObject.foo.classMethod() # classmethod
NSObject.foo.instanceMethod() # instanceMethod
That would make "NSObject.foo" a funny sort of object. In the current
scheme NSObject.foo is a perfectly normal object, either an unbound
method or a class method.
Except that it has a signature, isClassMethod, etc. It's not a
perfectly normal object, it's a selector instance. One of the big
"selling points" of ObjC is that you can pretty much translate ObjC
code to Python, this is a stumbling block I think we should work
around. With the "algorithm" I posted earlier, using these special
"classMethod / instanceMethod" accessors should never really be
necessary, except in rare instances where you are doing
SomeClass.someInstanceMethod(instance, ...) with varargs or something.

-bob
Jack Jansen
2003-11-07 09:36:14 UTC
Permalink
Post by Bob Ippolito
Post by Jack Jansen
Post by Bob Ippolito
NSObject.foo() # smart decision by the selector (smarter than
instance if it has one, see previous email)
NSObject.foo.classMethod() # classmethod
NSObject.foo.instanceMethod() # instanceMethod
That would make "NSObject.foo" a funny sort of object. In the current
scheme NSObject.foo is a perfectly normal object, either an unbound
method or a class method.
Except that it has a signature, isClassMethod, etc. It's not a
perfectly normal object, it's a selector instance. One of the big
"selling points" of ObjC is that you can pretty much translate ObjC
code to Python, this is a stumbling block I think we should work
around.
Hmm, you have a point. If I look at this then sometimes I agree with
you, sometimes I agree with myself. It really depends on whether you
take a Pythonic view or on ObjC-view.
--
Jack Jansen <***@cwi.nl> http://www.cwi.nl/~jack
If I can't dance I don't want to be part of your revolution -- Emma
Goldman
Bob Ippolito
2003-11-07 13:02:22 UTC
Permalink
Post by Jack Jansen
Post by Bob Ippolito
Post by Jack Jansen
Post by Bob Ippolito
NSObject.foo() # smart decision by the selector (smarter than
instance if it has one, see previous email)
NSObject.foo.classMethod() # classmethod
NSObject.foo.instanceMethod() # instanceMethod
That would make "NSObject.foo" a funny sort of object. In the
current scheme NSObject.foo is a perfectly normal object, either an
unbound method or a class method.
Except that it has a signature, isClassMethod, etc. It's not a
perfectly normal object, it's a selector instance. One of the big
"selling points" of ObjC is that you can pretty much translate ObjC
code to Python, this is a stumbling block I think we should work
around.
Hmm, you have a point. If I look at this then sometimes I agree with
you, sometimes I agree with myself. It really depends on whether you
take a Pythonic view or on ObjC-view.
Well the way I see it is this. Making selectors more magical will make
ObjC code work more often without "change". The only thing that
*might* break is doing SomeClass.someInstanceSelector(someInstance,
...) -- but people only really do that if someInstance is a string or
something weird is going on with PyObjC.. I really don't think this
change would cause any problems, and I don't think that the
classMethod/instanceMethod members would ever really need to be used,
but would be there as a backup plan in case for some reason PyObjC
guessed wrong and you really want to use a instance method that was
taken off the class. classMethod is only there for symmetry, it would
never be necessary.

-bob
b.bum
2003-11-07 14:04:16 UTC
Permalink
Unless I'm missing something, can't we always tell at the moment of
dispatch whether or not a particular method should be resolved in the
class or instance context?

When objc_msgSend() is invoked, the SEL parameter is resolved
internally based on the 'self' parameter -- on the target of
invocation. So, once we have an object reference and a selector, our
bridged dispatch to the method IMP should "just work" regardless of
whether it is a class or instance.

The only place that class vs. instance method is problematic would
appear to be within the Python subclasses of ObjC objects where there
is a need to define both a class and an instance method. I.e.
+description vs. -description.

Given that +description requires, effectively, a redeclaration through
the use of selector(), we could change the name of the method
slightly-- and invisibly-- at that time.

But that is ugly.

Instead, can we handle this at the time of dispatch?

I.e. if you do Foo.description to grab a reference to the description
method of the Foo class-- which has both +description and
-description-- then we should be able to figure out if the class or
instance implementation should be invoked at the time of dispatch based
on what the target of the invocation is.

There are other details that would have to be worked out. But, for the
most part, it would seem that this could be done in a largely automatic
and transparent fashion. The remaining unknowns are largely arbitrary
in nature because they only occur due to the combined interaction of
the ObjC and Python runtimes -- we are free to do whatever we want.

b.bum
Bob Ippolito
2003-11-07 14:21:43 UTC
Permalink
Post by b.bum
Unless I'm missing something, can't we always tell at the moment of
dispatch whether or not a particular method should be resolved in the
class or instance context?
When objc_msgSend() is invoked, the SEL parameter is resolved
internally based on the 'self' parameter -- on the target of
invocation. So, once we have an object reference and a selector, our
bridged dispatch to the method IMP should "just work" regardless of
whether it is a class or instance.
The only place that class vs. instance method is problematic would
appear to be within the Python subclasses of ObjC objects where there
is a need to define both a class and an instance method. I.e.
+description vs. -description.
No, it also happens when you are just using ObjC classes from the
Python side of the bridge. It *always* binds to the instance method,
nomatter what, if it has one. I'm not even worried about overriding
them from Python at the moment, just making ObjC classes usable as they
should be.
Post by b.bum
Given that +description requires, effectively, a redeclaration through
the use of selector(), we could change the name of the method
slightly-- and invisibly-- at that time.
But that is ugly.
Instead, can we handle this at the time of dispatch?
Sure, that's what I'm asking for :)
Post by b.bum
I.e. if you do Foo.description to grab a reference to the description
method of the Foo class-- which has both +description and
-description-- then we should be able to figure out if the class or
instance implementation should be invoked at the time of dispatch
based on what the target of the invocation is.
There are other details that would have to be worked out. But, for
the most part, it would seem that this could be done in a largely
automatic and transparent fashion. The remaining unknowns are
largely arbitrary in nature because they only occur due to the
combined interaction of the ObjC and Python runtimes -- we are free to
do whatever we want.
That's what I was proposing, however there is some ambiguity in
determining the target of the invocation if and only if we allow
SomeClass.instanceMethod(someInstance) -- but I proposed a way that
would figure that out most of the time, by counting the number of
arguments given if that selector exists for both the class and the
instance, which should work in every case that doesn't use varargs (do
any?).

-bob
b.bum
2003-11-07 15:46:11 UTC
Permalink
Post by Bob Ippolito
No, it also happens when you are just using ObjC classes from the
Python side of the bridge. It *always* binds to the instance method,
nomatter what, if it has one. I'm not even worried about overriding
them from Python at the moment, just making ObjC classes usable as
they should be.
Right -- and that is a bug.

"Binding" shouldn't occur until invocation except in the case where the
developer has explicitly invoked method via the unbound method
mechanism where they previously obtained a reference to the instance or
class version of a method.

I.e. if I have...

id foo;

foo = ... something ...;
[foo performSelector: @selector(description)];

... then the class or instance version of description will be invoked
depending on if foo is a class or instance object.
Post by Bob Ippolito
Post by b.bum
I.e. if you do Foo.description to grab a reference to the description
method of the Foo class-- which has both +description and
-description-- then we should be able to figure out if the class or
instance implementation should be invoked at the time of dispatch
based on what the target of the invocation is.
There are other details that would have to be worked out. But, for
the most part, it would seem that this could be done in a largely
automatic and transparent fashion. The remaining unknowns are
largely arbitrary in nature because they only occur due to the
combined interaction of the ObjC and Python runtimes -- we are free
to do whatever we want.
That's what I was proposing, however there is some ambiguity in
determining the target of the invocation if and only if we allow
SomeClass.instanceMethod(someInstance) -- but I proposed a way that
would figure that out most of the time, by counting the number of
arguments given if that selector exists for both the class and the
instance, which should work in every case that doesn't use varargs (do
any?).
If someInstance is an instance method, then the instance implementation
should be used... if it is a class method, then use the class
implementation. This most closely mimics the ObjC runtime.

If the developer specifically wants the class vs. instance version of
the method, then we should add API for querying for the one versus the
other. The API already exists on NSObject.

b.bum
Bob Ippolito
2003-11-07 16:18:04 UTC
Permalink
Post by b.bum
Post by Bob Ippolito
No, it also happens when you are just using ObjC classes from the
Python side of the bridge. It *always* binds to the instance method,
nomatter what, if it has one. I'm not even worried about overriding
them from Python at the moment, just making ObjC classes usable as
they should be.
Right -- and that is a bug.
"Binding" shouldn't occur until invocation except in the case where
the developer has explicitly invoked method via the unbound method
mechanism where they previously obtained a reference to the instance
or class version of a method.
I.e. if I have...
id foo;
foo = ... something ...;
... then the class or instance version of description will be invoked
depending on if foo is a class or instance object.
That's exactly what should happen, but that's not exactly how Python
works.. Basically, selectors are descriptor instances. When you get
the selector-instance-turned-descriptor from the class or instance it
calls __get__(self, fromInstanceOrNone, fromClass) on the selector
instance (tp_descr_get from the C API). So, the selector instance
needs to remember the values of fromInstanceOrNone and fromClass and
return an object (perhaps itself, but maybe not) that's suitable to be
introspected or called. So, when you dispatch this object, you know if
it was taken from the class or an instance (b/c fromInstanceOrNone
would be None if it was taken from the class).

In the current PyObjC, at this point it has already decided if it's
going to use a class or instance method. Before dispatch happens.

If you read back a couple posts I had a mock-Python Selector class that
shows how I would like the rules of the selector descriptor game to
work, which should fix this bug. The thing that I think you're
confused about is that it's not clearly obvious what the target of the
dispatch is.. obviously if the descriptor was taken from an instance,
you know you want to use the instance version. It gets more
complicated if you want to make these things act more like Python
objects, where you're allowed to do call instance methods off of the
class directly.. see my next paragraph.
Post by b.bum
Post by Bob Ippolito
Post by b.bum
I.e. if you do Foo.description to grab a reference to the
description method of the Foo class-- which has both +description
and -description-- then we should be able to figure out if the class
or instance implementation should be invoked at the time of dispatch
based on what the target of the invocation is.
There are other details that would have to be worked out. But, for
the most part, it would seem that this could be done in a largely
automatic and transparent fashion. The remaining unknowns are
largely arbitrary in nature because they only occur due to the
combined interaction of the ObjC and Python runtimes -- we are free
to do whatever we want.
That's what I was proposing, however there is some ambiguity in
determining the target of the invocation if and only if we allow
SomeClass.instanceMethod(someInstance) -- but I proposed a way that
would figure that out most of the time, by counting the number of
arguments given if that selector exists for both the class and the
instance, which should work in every case that doesn't use varargs
(do any?).
If someInstance is an instance method, then the instance
implementation should be used... if it is a class method, then use the
class implementation. This most closely mimics the ObjC runtime.
I should have said SomeClass.bothInstanceAndClassMethod(someInstance)
-- doing this method-from-class-using-instance-as-argument thing
creates a somewhat ambiguous case. Without *counting* the number of
arguments, you can't be sure whether you meant to call the instance
version or the class version. My suggested behavior breaks down when
the number of arguments can not be reliably counted (varags). I don't
know if this actually happens or not with the bridge, and even if it
did I would imagine that it would be extremely rare to find an ObjC
class that implements the same varargs selector for class and instance.
Post by b.bum
If the developer specifically wants the class vs. instance version of
the method, then we should add API for querying for the one versus the
other. The API already exists on NSObject.
It's currently possible but ugly and unintuitive (I didn't even know
about it, after looking at PyObjC's source code for hours). My
suggestion was to use:
SomeClass.bothInstanceAndClassMethod.instanceMethod(...)
instead of:
SomeClass.pyobjc_instanceMethods.bothInstanceAndClassMethod(...)

(note that this would *never* be needed if my proposed semantics were
implemented, *except* in the degenerate varargs case, if that case can
even exist).

-bob
Jack Jansen
2003-11-08 21:31:15 UTC
Permalink
Post by b.bum
Post by Bob Ippolito
No, it also happens when you are just using ObjC classes from the
Python side of the bridge. It *always* binds to the instance method,
nomatter what, if it has one. I'm not even worried about overriding
them from Python at the moment, just making ObjC classes usable as
they should be.
Right -- and that is a bug.
This hits the two-faced issue on the head: it's a bug if you look at
this with ObjC in mind, it's an essential shortcoming if you look at it
with Python in mind.

Bob said (at some point in the past of this discussion) something to
the effect that someclass.somemehod is a selector. If you view it from
that angle then the current behavior is a bug.
My initial thought (with my Python hat on) was that
someclass.somemethod is either a class method or an unbounded instance
method. Then this isn't a bug but a shortcoming that can't be overcome.
--
Jack Jansen, <***@cwi.nl>, http://www.cwi.nl/~jack
If I can't dance I don't want to be part of your revolution -- Emma
Goldman
Ronald Oussoren
2003-11-09 07:38:06 UTC
Permalink
Post by Jack Jansen
Post by b.bum
Post by Bob Ippolito
No, it also happens when you are just using ObjC classes from the
Python side of the bridge. It *always* binds to the instance
method, nomatter what, if it has one. I'm not even worried about
overriding them from Python at the moment, just making ObjC classes
usable as they should be.
Right -- and that is a bug.
This hits the two-faced issue on the head: it's a bug if you look at
this with ObjC in mind, it's an essential shortcoming if you look at
it with Python in mind.
Bob said (at some point in the past of this discussion) something to
the effect that someclass.somemehod is a selector. If you view it from
that angle then the current behavior is a bug.
My initial thought (with my Python hat on) was that
someclass.somemethod is either a class method or an unbounded instance
method. Then this isn't a bug but a shortcoming that can't be
overcome.
That's a nice summary of the issues.

Having thought about this a little I wouldn't mind if we changed the
selector code to be smarter about this, e.g.:

1) If the method is accessed through an instance it is always an
instance method (and if there is only a class method we raise an
AttributeError)
2) If the method is accessed through a class it is:
- an unbound method if it is only an instance method
- a bound method if it is only a class method
- a "smart" method if it is both an instance method and a class
method

Smart methods somehow detect how they are used (the number of arguments
passed in springs to mind, if you call an unbound instance method you
pass the self argument, if you call a bound class method you don't).

Other issues:
- if the class method has a different signature than the instance
method we cannot create a "smart" method, I'd fall back to the current
behaviour (e.g. return the instance method). It is highly unlikely that
this ever happens.
- It *must* be possible to get information about instance methods
through the class, otherwise we cannot create class browsers.
- If a "smart" method is overridden in a subclass, the subclass should
still contain a "smart" method.

Ronald

Loading...