Commits

Ronald Oussoren committed bfa82dc

Improved testing and documentation for functions in objc._descriptors

Documentation update is incomplete, not yet at 100% test coverage.

Comments (0)

Files changed (7)

 .DS_Store
 __pycache__
 autom4te.cache
+htmlcov
+.coverage
 
 syntax: glob
 *.so

pyobjc-core/Doc/lib/module-objc.rst

       The IBOutlet function is recognized by Interface Builder when it
       reads Python code.
 
-.. function:: IBAction
+.. function:: IBAction(function)
 
-   Mark an method as an action for use in Interface Builder. 
+   Mark an method as an action for use in Interface Builder.  Raises
+   :exc:`TypeError` when the argument is not a function.
    
    Usage:
 
            def alloc(self): 
                pass
 
+   .. note::
+
+      There is no function named *objc.classmethod*, use 
+      :func:`classmethod <__builtin__.classmethod>` to explictly mark a function
+      as a class method.
 
 
 .. function:: accessor
    Use this decorator on the definition of accessor methods to ensure
    that it gets the right method signature in the Objective-C runtime.
 
+   The conventions for accessor names that can be used with Key-Value Coding
+   is described in `the Apple documentation for Key-Value Coding`_
+
+   The table below describes the convention for methods for a property named '<property>',
+   with a short description and notes. The `Apple documentation for Key-Value Coding`_ 
+   contains more information.
+
+   ============== ============================ ===================================
+   Name           Description                  Notes
+   ============== ============================ ===================================
+   <property>     Getter for a basic property. 
+   -------------- ---------------------------- -----------------------------------
+   is<Property>   Likewise, for a boolean      PyObjC won't automaticly set the
+                  property.                    correct property type, use
+                                               :func:`typeAccessor` instead of
+                                               :func:`accessor`.
+   -------------- ---------------------------- -----------------------------------
+   set<Property>_ Setter for a basic property
+   -------------- ---------------------------- -----------------------------------
+   countOf<Property> Returns the number of
+                     items in a indexed 
+                     accessor, or unordered
+                     accessor
+   -------------- ---------------------------- -----------------------------------
+   objectIn<Property>AtIndex\_ Returns the
+                     object at a specific index
+                     for an indexed accessor
+   -------------- ---------------------------- -----------------------------------
+   <property>AtIndexes\_ Returns an array of    Note: don't use this with
+                     object values at specific :func:`typedAccessor`.
+                     indexes for an indexed    
+                     accessor. The argument    
+                     is an :c:type`NSIndexSet`.
+   -------------- ---------------------------- -----------------------------------
+   get<Property>_range_                           Optimized accessor                  Not supported by PyObjC, don't use
+   -------------- ---------------------------- -----------------------------------
+   insertObject_in<Property>AtIndex\_             Add an object to an indexed 
+                                                  accessor at a specific index.
+   -------------- ---------------------------- -----------------------------------
+   insert<Property>_atIndexes_                    Insert the values from a list of   Note: don't use this with 
+                                                  at specific indices. The           :func:`typedAccessor`.
+                                                  arguments are an :c:type:`NSArray` 
+                                                  and an :c:type:`NSIndexSet`.
+   -------------- ---------------------------- -----------------------------------
+   removeObjectFrom<Property>AtIndex\_            Remove the value
+                                                  at a specific index of an
+                                                  indexed accessor.
+   -------------- ---------------------------- -----------------------------------
+   remove<Property>AtIndexes\_                    Remove the values at specific
+                                                  indices of an indexed accessor. The 
+                                                  argument is an :c:type`NSIndexSet`.
+   -------------- ---------------------------- -----------------------------------
+   replaceObjectIn<Property>AtIndex_withObject\_  Replace the value at a specific
+                                                   index of an indexed accessor.
+   -------------- ---------------------------- -----------------------------------
+   replace<Property>AtIndexes_with<Property>_     Replace the values at specific
+                                                  indices of an indexed accessor.
+   -------------- ---------------------------- -----------------------------------
+   enumeratorOf<Property> Returns an :c:type:`NSEnumerator`
+                          for an unordered accessor.
+   -------------- ---------------------------- -----------------------------------
+   memberOf<Property>_ Returns True if the value is
+                       a member of an unordered accessor
+   -------------- ---------------------------- -----------------------------------
+   add<Property>Object\_ Insert a specific object in
+                       an unordered accessor
+   -------------- ---------------------------- -----------------------------------
+   add<Property>_       Add a set of new values
+                        to an unordered property.
+   -------------- ---------------------------- -----------------------------------
+   remove<Property>Object\_ Remove an object
+                         from an unordered property.
+   -------------- ---------------------------- -----------------------------------
+   remove<Property>_      Remove a set of objects
+                          from an unordered property.
+   -------------- ---------------------------- -----------------------------------
+   intersect<Property>_   Remove all objects from
+                          an unorderd property that
+                          aren't in the set argument.
+   ============== ============================ ===================================
+
+
+   .. versionchanged:: 2.5
+      Added support for unordered properties. Also fixed some issues for 64-bit
+      builds.
+
+.. _`the Apple documentation for Key-Value Coding`: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/AccessorConventions.html
+
 .. function:: typedAccessor(valueType)
 
    Use this decorator on the definition of accessor methods to ensure
 
    The *valueType* is the encoded string for a single value.
 
+   .. note:: 
+
+      When you use a typed accessor you must also implement "setNilValueForKey_",
+      as described in `the Apple documentation for Key-Value Coding`_
+
 .. function:: typedSelector
 
    Use this decorator to explicitly set the type signature for a method.
         def makeUnsignedIntegerOfDouble_(self, d):
            return d
    
+
+
 .. function:: namedSelector(name [, signature])
 
    Use this decorator to explictly set the Objective-C method name instead

pyobjc-core/Lib/objc/_descriptors.py

 This module defines the core interfaces of the Python<->Objective-C bridge.
 """
 
-__all__ = ['IBOutlet', 'IBAction', 'accessor', 'Accessor', 'typedAccessor', 'callbackFor', 'selectorFor', 'synthesize', 'namedselector', 'typedSelector', 'namedSelector' ]
+__all__ = ['IBOutlet', 'IBAction', 'accessor', 'Accessor', 'typedAccessor', 'callbackFor', 'selectorFor', 'synthesize', 'namedselector', 'typedSelector', 'namedSelector', 'instancemethod' ]
 
-from objc._objc import ivar, selector, _makeClosure, selector, _C_SEL, _C_ID
+from objc._objc import ivar, selector, _makeClosure, selector, _C_SEL, _C_ID, _C_NSUInteger, _C_NSBOOL
 import sys, textwrap
 import warnings
 from inspect import getargspec
 
+if sys.maxsize > 2**32:
+
+    _C_NSRange = "{_NSRange=QQ}"
+
+else:
+    _C_NSRange = "{_NSRange=II}"
+
 #
 # Interface builder support.
 #
     Return an Objective-C method object that can be used as an action
     in Interface Builder.
     """
+    if func is None:
+        raise TypeError("IBAction argument must be a callable")
     return selector(func, signature=b"v@:@")
 
 def instancemethod(func):
+    if func is None:
+        raise TypeError("instancemethod argument must be a callable")
     return selector(func, isClassMethod=False)
 
 def accessor(func, typeSignature=b'@'):
     
     if selArgs == 3:
         if funcName.startswith('validate') and funcName.endswith('_error_'):
-            return selector(func, signature=b'Z@:N^@o^@')
+            return selector(func, signature=objc._C_NSBOOL + b'@:N^@o^@')
 
         if funcName.startswith('insertObject_in') and funcName.endswith('AtIndex_'):
-            return selector(func, signature=b'v@:@i')
+            return selector(func, signature=b'v@:' + typeSignature + _C_NSUInteger)
         elif funcName.startswith('replaceObjectIn') and funcName.endswith('AtIndex_withObject_'):
-            return selector(func, signature=b'v@:i@')
-        
+            return selector(func, signature=b'v@:' + _C_NSUInteger + typeSignature)
+
+        elif funcName.startswith('get') and funcName.endswith('_range_'):
+            return selector(func, signature=b'v@:o^@' + _C_NSRange) 
+
+        elif funcName.startswith('insert') and funcName.endswith('_atIndexes_'):
+            return selector(func, signature=b'v@:@@') 
+
+        elif funcName.startswith('replaceObjects_in') and funcName.endswith('AtIndexes_'):
+            return selector(func, signature=b'v@:@@') 
+
+        elif funcName.startswith('replace') and 'AtIndexes_with' in funcName:
+            return selector(func, signature=b'v@:@@') 
+
+        elif funcName.startswith('replaceObject_in') and funcName.endswith('AtIndex_'):
+            return selector(func, signature=b'v@:' + typeSignature + _C_NSUInteger) 
+
         # pass through to "too many arguments"
 
     elif selArgs == 2:
         if funcName.startswith('objectIn') and funcName.endswith('AtIndex_'):
-            return selector(func, signature=b'@@:i')
+            return selector(func, signature=typeSignature + b'@:' + _C_NSUInteger)
         elif funcName.startswith('removeObjectFrom') and funcName.endswith('AtIndex_'):
-            return selector(func, signature=b'v@:i')
-        elif funcName.startswith('get') and funcName.endswith('_range_'):
-            return selector(func, signature=b'@@:{_NSRange=ii}')
+            return selector(func, signature=b'v@:' + _C_NSUInteger)
+        elif funcName.startswith('remove') and funcName.endswith('AtIndexes_'):
+            return selector(func, signature=b"v@:@")
+        elif funcName.endswith('AtIndexes_'):
+            return selector(func, signature=b"@@:@")
+        elif funcName.startswith('memberOf'):
+            return selector(func, signature=_C_NSBOOL + b"@:" + typeSignature)
+        elif funcName.startswith('add') and funcName.endswith('Object_'):
+            return selector(func, signature=b"v@:" + typeSignature)
+        elif funcName.startswith('add'):
+            return selector(func, signature=b"v@:@")
+        elif funcName.startswith('intersect'):
+            return selector(func, signature=b"v@:@")
 
         return selector(func, signature=b"v@:" + typeSignature)
 
     elif selArgs == 1:
-        if typeSignature == b'@' and func.__name__.startswith('countOf'):
-            typeSignature = 'i'
+        if funcName.startswith('countOf'):
+            typeSignature = _C_NSUInteger
+        elif funcName.startswith('enumerator'):
+            typeSignature = b"@"
+
 
         return selector(func, signature=typeSignature + b"@:")
 
 
 def typedSelector(signature):
     def _typedSelector(func):
+        if func is None:
+            raise TypeError("typedSelector() function argument must be a callable")
         return selector(func, signature=signature)
     return _typedSelector
 
 def namedSelector(name, signature=None):
     """
-    Python 2.4 decorator for overriding the Objective-C SEL for a method, usage:
+    Decorator for overriding the Objective-C SEL for a method, usage:
 
         @namedSelector("foo:bar:")
         def foobar(self, foo, bar):
     """
     if signature is not None:
         def _namedselector(func):
+            if func is None:
+                raise TypeError("IBAction argument must be a callable")
             return selector(func, selector=name, signature=signature)
     else:
         def _namedselector(func):
+            if func is None:
+                raise TypeError("IBAction argument must be a callable")
             return selector(func, selector=name)
 
     return _namedselector
 
 def typedAccessor(typeSignature):
     """
-    Python 2.4 decorator for creating a typed accessor, usage:
+    Decorator for creating a typed accessor, usage:
         
         @typedAccessor('i')
         def someIntegerAccessor(self):

pyobjc-core/NEWS.txt

 
     myLocation = objc.ivar(typer=NSPoint.__typestr__)
 
+- :func:`objc.IBAction` now raises TypeError when the argument is :data:`None`.
+
+- :func:`objc.instancemethod` is now actually exported by the :mod:`objc` package.
+
+- :func:`objc.accessor` and :func:`objc.typedAccessor` were not 64-bit safe.
+
+- :func:`objc.accessor` and :func:`objc.typedAccessor` didn't support the entire
+  set of KVC accessors.
+
 - Added :func:`objc.createStructAlias`, and deprecated 
   :func:`objc.registerStructAlias`. The new function has a "name" argument
   and can register types with the :class:`objc.ivar` type (see previous item)

pyobjc-core/PyObjCTest/test_descriptors.py

+from PyObjCTools.TestSupport import *
+
+import objc
+
+try:
+    from  Foundation import NSRange
+
+    _C_NSRange = NSRange.__typestr__
+
+except ImportError:
+    if sys.maxsize > 2 ** 32:
+        _C_NSRange = b"{_NSRange=QQ}"
+    else:
+        _C_NSRange = b"{_NSRange=II}"
+
+
+NSObject = objc.lookUpClass("NSObject")
+
+class TestBasicDescriptors (TestCase):
+
+    # IBOutlet is tested in test_ivar
+
+    def test_ibaction(self):
+        
+        @objc.IBAction
+        def myAction_(self, sender):
+            return 1
+
+        self.assertIsInstance(myAction_, objc.selector)
+        self.assertEqual(myAction_.signature, b'v@:@')
+        self.assertEqual(myAction_.selector, b'myAction:')
+        self.assertFalse(myAction_.isClassMethod)
+
+
+        self.assertRaises(TypeError, objc.IBAction, None)
+        self.assertRaises(TypeError, objc.IBAction, 42)
+
+    def test_instancemethod(self):
+        class TestDescriptorsClass1 (NSObject):
+            @objc.instancemethod
+            def new(self):
+                pass
+
+        o = NSObject.alloc().init()
+        self.assertFalse(hasattr(o, 'new'))
+        self.assertTrue(hasattr(NSObject, 'new'))
+        self.assertTrue(NSObject.new.isClassMethod)
+
+        o = TestDescriptorsClass1.alloc().init()
+        m = o.new
+        self.assertIsInstance(m, objc.selector)
+        self.assertFalse(m.isClassMethod)
+
+
+        self.assertRaises(TypeError, objc.instancemethod, None)
+        self.assertRaises(TypeError, objc.instancemethod, 42)
+
+    def test_typedSelector(self):
+        
+        @objc.typedSelector(b"I@:qq")
+        def mySelector_arg_(self, a, b):
+            return 4
+
+        self.assertIsInstance(mySelector_arg_, objc.selector)
+        self.assertEqual(mySelector_arg_.signature, b"I@:qq")
+        self.assertEqual(mySelector_arg_.selector, b"mySelector:arg:")
+
+        self.assertRaises(TypeError, objc.typedSelector(b"v@:i"), None)
+        self.assertRaises(TypeError, objc.typedSelector(b"v@:i"), 42)
+
+    def testNamedSelector(self):
+        @objc.namedSelector(b'foo:bar:')
+        def mymethod(self, a, b):
+            pass
+
+        self.assertIsInstance(mymethod, objc.selector)
+        self.assertEqual(mymethod.signature, b"v@:@@")
+        self.assertEqual(mymethod.selector, b"foo:bar:")
+
+        self.assertRaises(TypeError, objc.namedSelector(b"foo:bar:"), None)
+        self.assertRaises(TypeError, objc.namedSelector(b"foo:bar:"), 42)
+
+        @objc.namedSelector(b'foo:bar:', signature=b"q@:qq")
+        def mymethod(self, a, b):
+            pass
+
+        self.assertIsInstance(mymethod, objc.selector)
+        self.assertEqual(mymethod.signature, b"q@:qq")
+        self.assertEqual(mymethod.selector, b"foo:bar:")
+
+        self.assertRaises(TypeError, objc.namedSelector(b"foo:bar:", b"q@:qq"), None)
+        self.assertRaises(TypeError, objc.namedSelector(b"foo:bar:", b"q@:qq"), 42)
+
+    def testNamedselector(self):
+        @objc.namedselector(b'foo:bar:')
+        def mymethod(self, a, b):
+            pass
+
+        self.assertIsInstance(mymethod, objc.selector)
+        self.assertEqual(mymethod.signature, b"v@:@@")
+        self.assertEqual(mymethod.selector, b"foo:bar:")
+
+        self.assertRaises(TypeError, objc.namedselector(b"foo:bar:"), None)
+        self.assertRaises(TypeError, objc.namedselector(b"foo:bar:"), 42)
+
+        @objc.namedselector(b'foo:bar:', signature=b"q@:qq")
+        def mymethod(self, a, b):
+            pass
+
+        self.assertIsInstance(mymethod, objc.selector)
+        self.assertEqual(mymethod.signature, b"q@:qq")
+        self.assertEqual(mymethod.selector, b"foo:bar:")
+
+        self.assertRaises(TypeError, objc.namedselector(b"foo:bar:", b"q@:qq"), None)
+        self.assertRaises(TypeError, objc.namedselector(b"foo:bar:", b"q@:qq"), 42)
+
+    # synthesize is tested in test_synthesize
+
+    def test_accessor(self):
+        # NOTE: the optional type argument is tested through the typedAccessor function
+
+        # Basic properties:
+        
+        @objc.accessor
+        def color(self):
+            return 42
+
+        @objc.accessor
+        def isColor(self):
+            return 42
+
+        @objc.accessor
+        def setColor_(self, value):
+            pass
+
+        self.assertIsInstance(color, objc.selector)
+        self.assertIsInstance(isColor, objc.selector)
+        self.assertIsInstance(setColor_, objc.selector)
+
+        self.assertEqual(color.signature, b"@@:")
+        self.assertEqual(isColor.signature, b"@@:")
+        self.assertEqual(setColor_.signature, b"v@:@")
+
+
+        # Indexed accessors
+
+        @objc.accessor
+        def countOfFlavors(self):
+            return 2
+
+        @objc.accessor
+        def objectInFlavorsAtIndex_(self, idx):
+            return "sour"
+
+        @objc.accessor
+        def flavorsAtIndexes_(sef, indices):
+            return ["sour", "sweet"]
+
+        @objc.accessor
+        def getFlavors_range_(self, buffer, range):
+            return ["sour", "sweet"]
+
+
+        self.assertIsInstance(countOfFlavors, objc.selector)
+        self.assertIsInstance(objectInFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(flavorsAtIndexes_, objc.selector)
+        self.assertIsInstance(getFlavors_range_, objc.selector)
+
+        self.assertEqual(countOfFlavors.signature, objc._C_NSUInteger + b"@:")
+        self.assertEqual(objectInFlavorsAtIndex_.signature, b"@@:" + objc._C_NSUInteger)
+        self.assertEqual(flavorsAtIndexes_.signature, b"@@:@")
+
+        # XXX: This needs even more work: also have to add custom metadata!
+        self.assertEqual(getFlavors_range_.signature, b"v@:o^@" + _C_NSRange)
+
+        # Mutable Indexed Accessors
+
+        @objc.accessor
+        def insertObject_inFlavorsAtIndex_(self, value, idx):
+            pass
+
+        @objc.accessor
+        def insertFlavors_atIndexes_(self, values, indices):
+            pass
+
+        @objc.accessor
+        def removeObjectFromFlavorsAtIndex_(self, index):
+            pass
+
+        @objc.accessor
+        def removeFlavorsAtIndexes_(self, indices):
+            pass
+
+        @objc.accessor
+        def replaceObject_inFlavorsAtIndex_(self, value, idx):
+            pass
+
+        @objc.accessor
+        def replaceFlavorsAtIndexes_withFlavors_(self, indices, values):
+            pass
+
+
+        self.assertIsInstance(insertObject_inFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(insertFlavors_atIndexes_, objc.selector)
+        self.assertIsInstance(removeObjectFromFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(removeFlavorsAtIndexes_, objc.selector)
+        self.assertIsInstance(replaceObject_inFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(replaceFlavorsAtIndexes_withFlavors_, objc.selector)
+
+        self.assertEqual(insertObject_inFlavorsAtIndex_.signature, b"v@:@" + objc._C_NSUInteger)
+        self.assertEqual(insertFlavors_atIndexes_.signature, b"v@:@@")
+        self.assertEqual(removeObjectFromFlavorsAtIndex_.signature, b"v@:" + objc._C_NSUInteger)
+        self.assertEqual(removeFlavorsAtIndexes_.signature, b"v@:@")
+        self.assertEqual(replaceObject_inFlavorsAtIndex_.signature, b"v@:@" + objc._C_NSUInteger)
+        self.assertEqual(replaceFlavorsAtIndexes_withFlavors_.signature, b"v@:@@")
+
+
+        # Getter Unordered Accessors
+        @objc.accessor
+        def countOfLanguages(self):
+            pass
+
+        @objc.accessor
+        def enumeratorOfLanguages(self):
+            pass
+
+        @objc.accessor
+        def memberOfLanguages_(self, value):
+            return False
+
+        self.assertIsInstance(countOfLanguages, objc.selector)
+        self.assertIsInstance(enumeratorOfLanguages, objc.selector)
+        self.assertIsInstance(memberOfLanguages_, objc.selector)
+
+        self.assertEqual(countOfLanguages.signature, objc._C_NSUInteger + b"@:")
+        self.assertEqual(enumeratorOfLanguages.signature, b"@@:")
+        self.assertEqual(memberOfLanguages_.signature, objc._C_NSBOOL + b"@:@")
+
+        # Mutable Unordered Accessors
+        
+        @objc.accessor
+        def addLanguagesObject_(self, value):
+            pass
+
+        @objc.accessor
+        def addLanguagues_(self, values):
+            pass
+
+        @objc.accessor
+        def intersectLanguagues_(self, values):
+            pass
+
+        self.assertIsInstance(addLanguagesObject_, objc.selector)
+        self.assertIsInstance(addLanguagues_, objc.selector)
+        self.assertIsInstance(intersectLanguagues_, objc.selector)
+
+        self.assertEqual(addLanguagesObject_.signature, b"v@:@")
+        self.assertEqual(addLanguagues_.signature, b"v@:@")
+        self.assertEqual(intersectLanguagues_.signature, b"v@:@")
+
+    def test_typedAccessor(self):
+        # NOTE: the optional type argument is tested through the typedAccessor function
+
+        # Basic properties:
+
+        mytype = b"{Struct=qq}"
+        
+        @objc.typedAccessor(mytype)
+        def color(self):
+            return 42
+
+        @objc.typedAccessor(mytype)
+        def isColor(self):
+            return 42
+
+        @objc.typedAccessor(mytype)
+        def setColor_(self, value):
+            pass
+
+        self.assertIsInstance(color, objc.selector)
+        self.assertIsInstance(isColor, objc.selector)
+        self.assertIsInstance(setColor_, objc.selector)
+
+        self.assertEqual(color.signature, mytype + b"@:")
+        self.assertEqual(isColor.signature, mytype + b"@:")
+        self.assertEqual(setColor_.signature, b"v@:" + mytype)
+
+
+        # Indexed accessors
+
+        @objc.typedAccessor(mytype)
+        def countOfFlavors(self):
+            return 2
+
+        @objc.typedAccessor(mytype)
+        def objectInFlavorsAtIndex_(self, idx):
+            return "sour"
+
+        @objc.typedAccessor(mytype)
+        def flavorsAtIndexes_(sef, indices):
+            return ["sour", "sweet"]
+
+        @objc.typedAccessor(mytype)
+        def getFlavors_range_(self, buffer, range):
+            return ["sour", "sweet"]
+
+
+        self.assertIsInstance(countOfFlavors, objc.selector)
+        self.assertIsInstance(objectInFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(flavorsAtIndexes_, objc.selector)
+        self.assertIsInstance(getFlavors_range_, objc.selector)
+
+        self.assertEqual(countOfFlavors.signature, objc._C_NSUInteger + b"@:")
+        self.assertEqual(objectInFlavorsAtIndex_.signature, mytype + b"@:" + objc._C_NSUInteger)
+        self.assertEqual(flavorsAtIndexes_.signature, b"@@:@") #XXX: is this correct?
+
+        # XXX: This needs even more work: also have to add custom metadata!
+        self.assertEqual(getFlavors_range_.signature, b"v@:o^@" + _C_NSRange)
+
+        # Mutable Indexed Accessors
+
+        @objc.typedAccessor(mytype)
+        def insertObject_inFlavorsAtIndex_(self, value, idx):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def insertFlavors_atIndexes_(self, values, indices):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def removeObjectFromFlavorsAtIndex_(self, index):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def removeFlavorsAtIndexes_(self, indices):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def replaceObject_inFlavorsAtIndex_(self, value, idx):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def replaceFlavorsAtIndexes_withFlavors_(self, indices, values):
+            pass
+
+
+        self.assertIsInstance(insertObject_inFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(insertFlavors_atIndexes_, objc.selector)
+        self.assertIsInstance(removeObjectFromFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(removeFlavorsAtIndexes_, objc.selector)
+        self.assertIsInstance(replaceObject_inFlavorsAtIndex_, objc.selector)
+        self.assertIsInstance(replaceFlavorsAtIndexes_withFlavors_, objc.selector)
+
+        self.assertEqual(insertObject_inFlavorsAtIndex_.signature, b"v@:" + mytype + objc._C_NSUInteger)
+        self.assertEqual(insertFlavors_atIndexes_.signature, b"v@:@@") # XXX: is this correct?
+        self.assertEqual(removeObjectFromFlavorsAtIndex_.signature, b"v@:" + objc._C_NSUInteger)
+        self.assertEqual(removeFlavorsAtIndexes_.signature, b"v@:@")
+        self.assertEqual(replaceObject_inFlavorsAtIndex_.signature, b"v@:" + mytype + objc._C_NSUInteger)
+        self.assertEqual(replaceFlavorsAtIndexes_withFlavors_.signature, b"v@:@@") # XXX: is this correct?
+
+
+        # Getter Unordered Accessors
+        @objc.typedAccessor(mytype)
+        def countOfLanguages(self):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def enumeratorOfLanguages(self):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def memberOfLanguages_(self, value):
+            return False
+
+        self.assertIsInstance(countOfLanguages, objc.selector)
+        self.assertIsInstance(enumeratorOfLanguages, objc.selector)
+        self.assertIsInstance(memberOfLanguages_, objc.selector)
+
+        self.assertEqual(countOfLanguages.signature, objc._C_NSUInteger + b"@:")
+        self.assertEqual(enumeratorOfLanguages.signature, b"@@:")
+        self.assertEqual(memberOfLanguages_.signature, objc._C_NSBOOL + b"@:" + mytype)
+
+        # Mutable Unordered Accessors
+        
+        @objc.typedAccessor(mytype)
+        def addLanguagesObject_(self, value):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def addLanguagues_(self, values):
+            pass
+
+        @objc.typedAccessor(mytype)
+        def intersectLanguagues_(self, values):
+            pass
+
+        self.assertIsInstance(addLanguagesObject_, objc.selector)
+        self.assertIsInstance(addLanguagues_, objc.selector)
+        self.assertIsInstance(intersectLanguagues_, objc.selector)
+
+        self.assertEqual(addLanguagesObject_.signature, b"v@:" + mytype)
+        self.assertEqual(addLanguagues_.signature, b"v@:@") # XXX: is this correct?
+        self.assertEqual(intersectLanguagues_.signature, b"v@:@")
+
+if __name__ == "__main__":
+    main()

pyobjc-core/PyObjCTest/test_ivar.py

     
         o.var2 = 4
         self.assertIsInstance(o.var2, float)
-    
+
+    def testNamedOutlet(self):
+        class NamedOutlet (NSObject):
+            outlet1 = objc.IBOutlet()
+            outlet2 = objc.IBOutlet("my_outlet")
+
+        all_outlets = {}
+
+        for name, tp in objc.listInstanceVariables(NamedOutlet):
+            all_outlets[name] = tp
+
+        self.assertEqual(all_outlets['outlet1'], objc._C_ID)
+        self.assertEqual(all_outlets['my_outlet'], objc._C_ID)
+
+        o = NamedOutlet.alloc().init()
+        self.assertTrue(hasattr(o, 'outlet1'))
+        self.assertTrue(hasattr(o, 'outlet2'))
+
 
 if __name__ == '__main__':
     main()

pyobjc-core/PyObjCTest/test_synthesize.py

         obj.setSomeTitle_(v)
         self.assertEqual(obj.someTitle(), 42)
 
+    def testFailures(self):
+        self.assertRaises(ValueError, objc.synthesize, '')
+        self.assertRaises(ValueError, objc.synthesize, None)
+
 if __name__ == "__main__":
     main()