Objective-Everything Release 5.  Copyright ©1994-1998 by TipTop Software, Inc.  All Rights Reserved.

  Objective-Python

Object Representation

There are two types of objects: native Python objects and ObjPy objects (really Objective-Everything objects: ObjC, ObjTcl, ObjPerl, ...).  ObjPy objects are represented as instances of ObjPy*Object in the Python domain.

 o=Class.NSObject.alloc().init().autorelease() 
 print o  # ==> <Object: 0x...> 
 print type(o)  # ==> <type 'ObjPyObject'> 

Symmetrically, native Python objects are represented as instances of TTPyObject in the Objective-Everything domain.  Consider the following implementation:

 @implementation MyObject:NSObject 
 + (id)makeObject:(id)o perform:(SEL)sel with:(id)arg 
 { 
   return [o perform:sel with:arg]; 
 } 
 @end 

Now:

 class XYZ: 
   def hello(self): 
     return "Hello World!" 
 p=XYZ() 
 print p  # ==> <XYZ instance at ...> 
 print Class.MyObject.makeObject_perform_with_(p,'description',nil) 
   # ==> <PyObject: <XYZ instance at ...>> 
 print Class.MyObject.makeObject_perform_with_(p,'class',nil) 
   # ==> TTPyObject 
 print Class.MyObject.makeObject_perform_with_(p,'hello',nil) 
   # ==> Hello World! 

The nil object is represented as nil.  Class objects can be obtained as attributes of Class, attributes of C, using the class_lookup() or class_get() methods.

 NSObject=Class.NSObject 
 NSObject=class_get("NSObject") 
 NSObject=class_lookup("NSObject") 

Note: You never need to retain/release ObjPy objects.  The Objective-Python system does it automatically behind the scenes!  As a consequence, to force a value to be released, just assign it a NULL value:

 x=Class.NSObject() 
 # ... 
 x=None  # x is now gone 

 # To release instance variables in a dealloc method: 
 def dealloc(self): 
   """-(void)dealloc;""" 
   self['anObject']=None 
   self['aCString']=None 
   # ... 
   super.dealloc() 

Method Invocation

ObjC: obj is an id Python: obj is an ObjPy object w/selmap
[obj message] obj.message() obj.message()
[obj message:a1] obj.message_(a1) obj.message(a1)
[obj message:a1 with:a2] obj.message_with_(a1,a2) obj.messageWith(a1,a2)

Note: In ObjPython, colon characters (:) in selector names are replaced with underscores (_).  If the selector name has any underscores (_) other than the leading underscores, you must use the msg_send() method directly.

 obj.doSomething()              # OK:  selector="doSomething" 
 obj._doSomething()             # OK:  selector="_doSomething" 
 obj.doSomething_(a1)           # OK:  selector="doSomething:" 
 obj.doSomething_with_(a1,a2)   # OK:  selector="doSomething:with:" 
 # To invoke "do_something:with:" 
 obj.do_something_with_(a1,a2)  # NOT OK!! selector="do:something:with:" 
 obj.msg_send("do_something:with:",a1,a2) # OK 
 Method(obj,"do_something:with:")(a1,a2)  # OK 

Super and Ex messages

Super.  To invoke a method implemented in a specific class, or its superclass, use msg_send_super():

 obj.msg_send_super(clazz,selector,args...) 

where obj is the target object, selector is a selector for method being invoked, clazz is a class in which a method is defined (the class must be the objects class or some superclass), and args... is the argument list.  Note that obj must be a kind of clazz, otherwise a runtime exception is raised.

For example, to invoke -aSelector:with: in  obj's superclass:

 super_class=obj.superclass()

 obj.msg_send_super(super_class,'aSelector:with:',a1,a2) 

 # or: 
 Method(obj,'aSelector:with:',super_class)(a1,a2) 

Inside a method body, to send a message to a superclass, you can simply use super:

 super.doSomething(args...) 

Ex.  To invoke a method implemented in a specific category of a class, use msg_send_ex():

 obj.msg_send_ex(clazz,idx,selector,args...) 

where obj is the target object, selector is a selector for method being invoked, clazz and idx specify category in which the method is defined, and args... is the argument list.  Note that obj must be a kind of clazz, otherwise a runtime exception is raised.  The idx argument has the following meaning:

In addition, inside a method body you can use ex to send 'ex' messages.  E.g.:

 def doSomething(self): 
   """-(id)doSomething;""" 
   ex.doSomething()  # invoke previous doSomething implementation 
   # ... 
   return self 

Selector mapping

Same as in ObjPerl; see the ObjPerl documentation for details.  Selector mapping is ON by default. A secret recipe for figuring out selector names for Python method names:

 m=Method(nil,'clazz') 
 print "%s(%s)" % (m.__name__, m.argc) 
   # ==> class(2) 
 m=Method(nil,'performWithWith') 
 print "%s(%s)" % (m.__name__, m.argc) 
   # ==> perform:with:with:(3) 
 m=Method(nil,'aSeLeCtoR')  # an unknown selector 
 m.argc=4  # argument count hint 
 print "%s(%s)" % (m.__name__, m.argc) 
   # ==> aSeLeCtoR::(4) 

At startup, selector map is read from file ObjCore.framework/Resources/ObjCore.selmap

Constructor mapping

Creating instances the standard Python way works just as expected.  E.g.:

 o=Class.NSObject() 
 d=Class.NSMutableDictionary()  # d=Class.NSMutableDictionary.dictionary() 
 s=Class.NSString("hello")  # s=Class.NSString.stringWithCString("hello") 
   # s is a plain Python string 

The default constructor map is read from file ObjCore.framework/Resources/ObjCore.conmap

Type Conversion

When a message is sent from Python to an ObjPy object, all native Python arguments are converted to the appropriate low-level ObjC types.  The value returned is converted back to the appropriate Python value.

Primitive ObjC type Python type
Class ObjPyClassObject
id ObjPy*Object   (***)
SEL String
STR String
char Int/String
unsigned char Int/String
short Int
unsigned short Int
int Int
unsigned int Int
long Int
unsigned long Int
bitfield Int
float Float
double Float
array Tuple/List
struct Tuple/List
pointer C Datum

*** Object type conversion: id <--> Py conversion

id --> Python conversion

Class default py()/autoconvert
TTPyObject Native -->
NSString String -->
NSMutableString String -->
NSNumber Int/Float -->
NSArray ObjPySeqObject List
NSDictionary ObjPyMapObject Dictionary
Class ObjPyClassObject -->
other id ObjPyObject -->
Examples
 print type(Class.NSString("abc"))  # ==> <type 'string'> 
 print type(Class.NSMutableString("xyz"))  # ==> <type 'string'> 
 print type(Class.NSNumber(123))  # ==> <type 'int'> 
 print type(Class.NSDictionary())  # ==> <type 'ObjPyMapObject'> 
 print type(Class.NSObject())  # ==> <type 'ObjPyObject'> 

Python --> id conversion

Py Class default obj()
ObjPy*Object id -->
String NSString -->
Int/Float NSNumber -->
Tuple/List TTPyObject NSMutableArray
Dictionary TTPyObject NSMutableDictionary
other py TTPyObject -->

Explicit object type conversion

obj(py)

Converts Python object py to its ObjPy equivalent.  E.g., Python lists are converted to NSArrays, Python dictionaries to NSDictionaries, etc.  If py is already an ObjPy object, this function just returns it.

 a=obj(['one', 2, 'three']) 
 print repr(a)  # ==> <NS*Array: 0x...> 
 print a.clazz()  # ==> NS*Array 
 print type(a)  # ==>  <type 'ObjPy*Object'> 
 d=obj({'one' : 1,  'two' : 'abc' }) 
 print d  # ==> '{one = 1; two = abc; }' 
 print obj(nil)  # ==> nil 
 print obj(Class.Object)  # ==>  Object 
py(obj)

Converts ObjPy object obj to its Python equivalent.  E.g., NSArrays are converted to Python lists, NSDictionaries to Python dictionaries, etc.  If obj is a plain Python object, this function just returns it.  This is the inverse of the obj() function.

 print py(obj(['one', 2, 'three']))  # ==> ['one', 2, 'three'] 
 print py(obj({'one' : 1,  'two' : 'abc' }))  # ==> {'one': 1, 'two': 'abc'} 
 print type(py(123))  # ==> <type 'int'> 

Instance Variable Access

ObjPyObject behaves like a dictionary with elements of the dictionary being the actual object's instance variables.  For example:

 o=Class.MyObject() 
 print o['isa'] 
 print getattr(o,'aStr') 
 o['aStr']="Hello World!" 
 setattr(o,'anInt',123) 
 print len(o) 
 print len(nil) 
 if o: print "non-nil" 

In addition, ObjPyObject implements the following methods relevant to instance variable access:

has_key(k), keys(), values(), items() 

Example:

print o.items() 

An alternate way of accessing ivars is via the instance variable accessor object: o.__iv__

Example:

 print o.__iv__.aStr 
 print o.__iv__.__members__ 
 print o.__iv__.__types__ 

In addition to instance variables, Objective-Everything objects can have any number of attribute values associated with it.  Attribute values are of type "id".

Example:

 o.__xv__.anXv='Hello World!' 
 print o.__xv__.anXv 
 print o.__xv__.__members__ 

Class Define

A. subclass+category

To create a subclass in Objective-Everything, use the subclass method:

 classobject.subclass(name[, instance_vars]) 

Example:

 Foo=Class.Object.subclass('Foo','int anInt; char *aString; id anObj;') 
 foo=Foo() 
 print foo.items() 
 # ==> [('isa', ObjPy.Class.Foo), ('anInt', 0), ('aString', None), ('anObj', ObjPy.nil)] 

To declare and define methods, use the class' category method:

 classobject.category(name, methods [, protocol_list]) 

You can use extend as a synonym for category.

The methods argument may be either a dictionary, a sequence, or a Python class object:

A.1.

If the methods argument is a dictionary, the keys are method prototypes, and corresponding values are method implementations (callable objects).  Example:

 def fun0(self): 
   return ('--fun0--', self) 
 def fun1(self, a1): 
   return ('--fun1--', self, a1) 
 def fun1cmd(self, _cmd, a1): 
   # If the second arg name is _cmd, it will set to the selector name 
   return ('--fun1cmd--', self, _cmd, a1) 
 def fun(self, *args): 
   return ('--fun--', self, args) 
 Class.Foo.category('MyCategory1', { 
   '-(id)foo;' : fun0, 
   '-(id)bar:(int)x' : fun1cmd, 
   '-(id)baz:(const char *)x double:(double)d;' : fun 
  }) 
  print foo.foo()  # ==> ('--fun0--', <Foo: 0x...>) 
  print foo.bar(123)  # ==> ('--fun1cmd--', <Foo: 0x...>, 'bar:', 123) 
  print foo.baz_double_('baz', 4.5)  # ==>  ('--fun--', <Foo: 0x...>, ('baz', 4.5)) 

A.2.

If the methods argument is a sequence, its elements should be method implementations (callable objects).  In this case prototypes can be explicitly given in a doc string.  Otherwise, the system automatically generates the method name based on the callable name and the number of arguments, where each argument type is id and return type is id. Example:

 def fun1doc(self,a1): 
   """-(id)docBar:(int)x; 
      Put your method doc here...""" 
   return ('--fun1doc--', self, a1) 
 Class.Foo.category('MyCategory2', [fun1doc, fun0, fun1cmd] ) 
 print foo.docBar(456)  # ==> ('--fun1doc--', <Foo: 0x...>, 456) 
 print foo.fun1cmd('xyz')  # ==> ('--fun1cmd--', <Foo: 0x...>, 'fun1cmd:', 'xyz') 

A.3.

If the methods argument is a Python class object, this is equivalent to the #2 with the method sequence being all methods defined in the class.  Example:

 class XYZ: 
   def meth1(self,_cmd,a): 
     """-methFoo:(const char *)a;""" 
     return ('--meth1--',self,_cmd,a) 
   def meth_with_(self,_cmd,x,y): 
     return ('--meth_with_--', self, _cmd, x, y) 
   def __str__(self): 
     return '<Foo@'+hex(hash(self))+'>' 
   def __init__(self): 
     # Py initialization... 
     pass 
   def __del__(self): 
     # Py descrution... 
     pass 
   def pyonly(self): 
     """@skip;""" 
     return ('---pyonly--',self) 
 Class.Foo.category('MyCategory3', XYZ) 
 print foo.methFoo('bar')  # ==> ('--meth1--', <Foo: 0x...>, 'methFoo:', 'bar') 
 print foo.methWith('abc',123) 
   # ==> ('--meth_with_--', <Foo: 0x...>, 'meth:with:', 'abc', 123) 
 print foo.description()  # ==> <Foo@0x...> 
 foo.msg_send('pyonly')  # croaks: <Foo: 0x...> does not respond to -pyonly 

Note: Some Python methods have special behavior.  Method __str__ maps to selector -description.  Methods __init__ and __del__ do not map to any selectors.

Now:

 print unparse(Class.Foo) 
 @interface Foo : Object 
 // Subclasses: None 
 { 
   int anInt; 
   STR aString; 
   id anObj; 
 } 
 // Class methods 
 // Instance methods 
 -(id)description;      ---------\ 
 -(id)methFoo:(STR)a0;            } MyCategory3 (A.3) 
 -(id)meth:(id)a0 with:(id)a1; --/ 
 -(id)docBar:(int)a0;   --------------\ 
 -(id)fun0;                            } MyCategory2 (A.2) 
 -(id)fun1cmd:(id)a0;   --------------/ 
 -(id)bar:(int)a0;      -------------------\ 
 -(id)foo;                                  } MyCategory1 (A.1) 
 -(id)baz:(STR)a0 double:(double)a1; ------/ 
 @end 

B. cdefine+bind

Another way of defining classes, categories, and specifying method implementations is using the cdefine and cinclude functions:

 cdefine(decl) 
 cinclude(filename) 

The cdefine function parses C-style class/category declarations and generates the classes, categories, and protocols. Methods in categories will have NULL implementations.  The second step is to bind to concrete method implementations.  Example:

 cdefine('\ 
   @interface Foo(Object)\n\ 
   -moreMethods;\n\ 
   @end') 
 foo.moreMethods()  # ==> croaks: instance ObjPy method not implemented: Class=Foo, selector=moreMethods 
 cinclude('Bar.h') 

Where file Bar.h is:

 // File: Bar.h 
 @protocol BarProto 
 -barMethod; 
 @end 
 @interface Bar:NSObject <BarProto> 
 { 
   double aDouble; 
   NSRect aRect; 
   NSString *aStrObj; 
 } 
 -barMethod; 
 -(void)hello; 
 @end 
 // EOF 

Now, the second step is to bind method implementation using bind:

 classobject.bind(methods) 

where the methods argument can be a dictionary, a sequence, or a Python class object, just like in the category command.
Example:

 class XYZ: 
   def hello(self): 
     return ('--hello--') 
   # ... 
 Class.Bar.bind(XYZ) 
 bar=Class.Bar() 
 print bar.hello()  # ==> None  (since hello returns void as specified in Bar.h) 

C. Py subclass

In addition, the ObjPy system lets you graft plain Python classes into Objective-Everything class hierarchy.  This approach puts a minimal overhead on the Python programmer.  The syntax is:

 classobject.subclass(pyclass[, instance_vars]) 

The effect is that the system creates a counterpart Obj-Everything class for the specified Python class.  The counterpart class and the native Python class are said to be "bound".

Example:

 class ABC: 
   def abc(self,a): 
     return ('--abc--',self,a) 
 class Baz(ABC): 
   def xyz(self,a): 
     """-bar:(int)i;""" 
     return ('--xyz--',self,a) 
 def foo(self,a,b): 
   return ('--foo--',self,a,b) 
 Class.Object.subclass(Baz) 
baz=Baz()
 print baz  # ==> {<Baz instance at ...>=<Baz: 0x...>} 
 print Class.Object.Baz==Baz  # ==> 1 
 # Python side invocation: 
 print baz.xyz('abc')  # ==> ('--xyz--', ..., 'abc') 
 print baz.bar(123)  # ==> ('--xyz--', ..., 123) 
 print baz.clazz()  # ==> {<class Baz at ...>=ObjPy.Class.Baz} 
 print baz.abc('def')  # ==> ('--abc--', ..., 'def') 
 # ObjC side invocation: 
 perform=Class.MyObject.makeObject_perform_with_ 
 print perform(baz,'description',nil) 
   # ==> <Baz: 0x...> 
 print perform(baz,'class',nil) 
   # ==> {<class Baz at ...>=ObjPy.Class.Baz} 
 print perform(baz,'superclass',nil) 
   # ==> Object 
 print perform(baz,'xyz',nil) 
   # ==> croaks: <Baz: 0x...> does not respond to -xyz 
 print perform(baz,'abc:','xyz') 
   # ==> ('--abc--', ..., 'xyz') 

Python subclasses of a bound class are also bound.  Example:

 class MoreBaz(Baz): 
   def foo(self,a,b): 
     return ('--Baz::foo--',super.foo(a,b)) 
 baz2=MoreBaz() 
 # Python side invocation: 
 print baz2.foo('xyz',987)  # ==> ('--Baz::foo--', ('--foo--', ..., 'xyz', 987)) 
 # ObjC-side invocation 
 perform2=Class.MyObject.makeObject_perform_with_with_ 
 print perform2(baz2,'foo::', 'one','two') 
   # ==> ('--Baz::foo--', ('--foo--', ..., 'one', 'two')) 
 print perform(baz2,'class',nil) 
   # ==> {<class MoreBaz at ...>=ObjPy.Class.MoreBaz} 
 print perform(baz2,'superclass',nil) 
   # ==> {<class Baz at ...>=ObjPy.Class.Baz} 

Now:

 print unparse(Class.MoreBaz) 
 @interface MoreBaz : Baz 
 { 
 } 
 // Class methods 
 +(id)_allKeys; 
 // Instance methods 
 -(id)foo:(id)a0 :(id)a1; 
 @end 

Note: The subclass command will fail if you attempt to multiply-inherit an Obj-Everything class.  Example:

 class A: pass 
 class B(A): pass 
 class C(B): pass 
 class D: pass 
 Class.Object.subclass(B) 
 Class.Object.subclass(C)  # croaks: Python class C is already bound... 
 Class.List.subclass(C)    # croaks: ditto 
 B.subclass(C)  # croaks: ditto 
 B.subclass(D)  # OK 
 B.subclass(A)  # croaks: circular class hierarchy: B is a Python subclass of A 

Note: Once you 'graft' a Python class into Obj-Everything class hierarchy, you can do anything with it that you can do with any other Obj-Everything class.  E.g., you can define a category on such a class, implement methods in other Objective-Everything languages, etc.

Declaring ObjC-style variables for a Python-bound class

The following functionality is also included; it is subject to change and may be removed in a future release.  Instance variables can be specified in the class docstring. Obj-Everything-side ivar prototype is given in @{ ... }, and Python-side instance attributes are given in @[ ... ]. Example:

 class XYZ: 
   """ @{ int anInt; char *str; }; 
       @[ one two three ]; 
   """ 
   pass 
 Class.Object.subclass(XYZ)  # bind XYZ 
 x=XYZ() 
 print x.keys()  # ==> ['isa', 'anInt', 'str'] 
 print x.__iv__.__types__  # ==> {'str': '*', 'anInt': 'i', 'isa': '#'} 
 CFun.declare('ObjPy_declaredKeys','@@:') 
 print CFun.ObjPy_declaredKeys(x,'declaredKeys')  # ==> (one, two, three) 

Error System

The ObjPy system defines two Python exceptions: ObjPyError, ObjPyException

 try: Class.NSException.raise_format_("MyException","just testing...") 
 except ObjPyException, x: 
   print x  # ==> just testing... 
   print x.clazz()  # ==> NSException 
   print x.name()  # ==> MyException 
   print x.reason()  # ==> just testing... 
   print x.userInfo()  # ==> nil 

Symmetrically, Python exceptions are received as NSException in the ObjC domain.


[previous][contents][next]