Commits

Anonymous committed 642063e

Lots of changes to the convenience methods.

  • Participants
  • Parent commits cef7bd4

Comments (0)

Files changed (6)

 2002-10-30  Bill Bumgarner  <bbum@codefab.com>
+	* Many, many changes to the convenience methods in the objc
+	module.  Wrote a bunch of tests in test_nsarray and
+	test_nsdictionary.
+
 	* Fixed a bug in the NSDraggingDestination method declarations and
 	made all of the methods optional (the developer only needs to
 	implement a subset of the methods and none of the methods are

pyobjc/Lib/Foundation/test/test_nsarray.py

+import unittest
+import objc
+
+from Foundation import *
+
+class TestNSArrayInteraction( unittest.TestCase ):
+    def testRepeatedAllocInit( self ):
+        for i in range(1,1000):
+            a = NSArray.alloc().init()
+
+    def testIndices( self ):
+        x = NSArray.arrayWithArray_( ["foo", "bar", "baz"] )
+
+        self.assertEquals( x.indexOfObject_("bar"), 1 )
+
+        self.assertRaises( IndexError, x.objectAtIndex_, 100)
+
+    def testContains( self ):
+        x = NSArray.arrayWithArray_( ["foo", "bar", "baz"] )
+        self.assertEquals( x.count(), 3 )
+        self.assertEquals( len(x), 3 )
+
+        self.assert_( x.containsObject_("foo") )
+        self.assert_( not x.containsObject_("dumbledorf") )
+
+        self.assert_( "foo" in x )
+        self.assert_( not "dumbledorf" in x )
+
+    def testIn( self ):
+        x = NSMutableArray.array()
+        for i in range(0, 100):
+            x.addObject_(i)
+
+        y = []
+        for i in x:
+            y.append( i )
+
+        z = []
+        for i in range(0, 100):
+            z.append( i )
+
+        self.assertEquals(x, y)
+        self.assertEquals(x, z)
+        self.assertEquals(y, z)
+
+        for i in range(0, 100):
+            self.assert_( i in x )
+
+        self.assert_( 101 not in x )
+        self.assert_( None not in x )
+        self.assert_( "foo bar" not in x )
+
+    def assertSlicesEqual(self,  x, y, z):
+        self.assertEquals( x, x[:] )
+        self.assertEquals( y, y[:] )
+        self.assertEquals( z, z[:] )
+        
+        self.assertEquals( x[25:75], y[25:75] )
+        self.assertEquals( x[25:75], z[25:75] )
+        self.assertEquals( y[25:75], z[25:75] )
+
+        self.assertEquals( x[:15], y[:15] )
+        self.assertEquals( x[:15], z[:15] )
+        self.assertEquals( y[:15], z[:15] )
+
+        self.assertEquals( x[15:], y[15:] )
+        self.assertEquals( x[15:], z[15:] )
+        self.assertEquals( y[15:], z[15:] )
+
+        self.assertEquals( x[-15:], y[-15:] )
+        self.assertEquals( x[-15:], z[-15:] )
+        self.assertEquals( y[-15:], z[-15:] )
+
+        self.assertEquals( x[-15:30], y[-15:30] )
+        self.assertEquals( x[-15:30], z[-15:30] )
+        self.assertEquals( y[-15:30], z[-15:30] )
+
+        self.assertEquals( x[-15:-5], y[-15:-5] )
+        self.assertEquals( x[-15:-5], z[-15:-5] )
+        self.assertEquals( y[-15:-5], z[-15:-5] )
+
+    def testSlice( self ):
+        x = NSMutableArray.array()
+        for i in range(0, 100):
+            x.addObject_(i)
+
+        y = []
+        for i in x:
+            y.append( i )
+
+        z = []
+        for i in range(0, 100):
+            z.append( i )
+
+        self.assertSlicesEqual(x, y, z)
+
+        k = range(300, 50)
+        x[20:30] = k
+        y[20:30] = k
+        z[20:30] = k
+
+        self.assertSlicesEqual(x, y, z)
+
+        # Note that x[1] = x works in python, but not for a bridged NS*Array*.
+        # Not sure if there is anything we can do about that.
+        x[1] = x[:]
+        y[1] = y[:]
+        z[1] = z[:]
+
+        self.assertSlicesEqual(x, y, z)
+
+        del x[-15:-5]
+        del y[-15:-5]
+        del z[-15:-5]
+
+        self.assertSlicesEqual(x, y, z)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest( unittest.makeSuite( TestNSArrayInteraction ) )
+    return suite
+
+if __name__ == '__main__':
+    unittest.main( )

pyobjc/Lib/Foundation/test/test_nsdictionary.py

+import unittest
+import objc
+
+from Foundation import *
+
+class TestNSDictionaryInteraction( unittest.TestCase ):
+    def testRepeatedAllocInit( self ):
+        for i in range(1,1000):
+            d = NSDictionary.alloc().init()
+
+    def testBasicInteraction( self ):
+        d = NSMutableDictionary.dictionary()
+        d['a'] = "foo"
+        d['b'] = "bar"
+
+        self.assertEqual(d['a'], "foo", "Failed to retrieve the same thing that was put into the dict.")
+        try:
+            d['c']
+            self.fail("Should have raised...")
+        except KeyError:
+            pass
+
+    def testIn( self ):
+        d = NSMutableDictionary.dictionary()
+        d['a'] = "foo"
+        d['b'] = "bar"
+        d[1] = "baz"
+        d[0] = "bob"
+        # d[-1] = None -- this fails because the bridge doesn't proxy py(None) to objc(NSNull)... not sure if it should
+
+        self.assert_( 'a' in d )
+        self.assert_( 1 in d )
+        # self.assert_( -1 in d )
+        # self.assert_( d[-1] is None )
+        self.assert_( 'q' not in d )
+
+        for k in d.allKeys():
+            self.assertEqual( d.objectForKey_( k ), d[k] )
+
+        for k in d:
+            self.assertEqual( d.objectForKey_( k ), d[k] )
+            
+        del d['a']
+        self.assert_( 'a' not in d )
+        
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest( unittest.makeSuite( TestNSDictionaryInteraction ) )
+    return suite
+
+if __name__ == '__main__':
+    unittest.main( )

pyobjc/Lib/Foundation/test/test_nsexception.py

+import unittest
+import objc
+
+from Foundation import *
+
+class TestNSExceptionInteraction( unittest.TestCase ):
+    def testRepeatedAllocInit( self ):
+        for i in range(1,1000):
+            a = NSException.alloc().initWithName_reason_userInfo_( "Bogus", "A bad reason", { "foo" : "bar" } )
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest( unittest.makeSuite( TestNSExceptionInteraction ) )
+    return suite
+
+if __name__ == '__main__':
+    unittest.main( )

pyobjc/Lib/objc/_convenience.py

+from __future__ import generators
+
 """
 This module implements a callback function that is used by the C code to
 add Python special methods to Objective-C classes with a suitable interface.
 TODO:
 - Add external interface: Framework specific modules may want to add to this.
 """
-from objc import set_class_extender, selector
+from objc import set_class_extender, selector, runtime
 
 CONVENIENCE_METHODS = {}
 CLASS_METHODS = {}
 # NOTE: the '!= 0' in the definition of the comparison function
 # is there to force conversion to type 'bool' on Python releases
 # that have such a type.
-def __contains__(self, elem):
-	return self.containsObject_(elem) != 0
-def __eq__1(self, other):
-	return self.isEqualTo_(other) != 0
-def __eq__2(self, other):
-	return self.isEqual_(other) != 0
-def __ne__(self, other):
-	return not (self.isEqual_(other) != 0)
-def __gt__(self, other):
-	return self.isGreaterThan_(other) != 0
-def __ge__(self, other):
-	return self.isGreaterThanOrEqualTo_(other) != 0
-def __lt__(self, other):
-	return self.isLessThan_(other) != 0
-def __le__(self, other):
-	return self.isLessThanOrEqualTo_(other) != 0
-def __repr__(self):
-	return self.description()
-def __len__1(self):
-	return self.count()
-def __len__2(self):
-	return self.length()
-def __getitem__1(self, key):
+
+def __getitem__objectForKey(self, key):
 	res = self.objectForKey_(key)
-	if res == None:
+	if res is None:
 		raise KeyError, key
 	return res
-def has_key_1(self, key):
+def has_key_objectForKey(self, key):
 	res = self.objectForKey_(key)
 	return not (res is None)
-def get_1(self, key, dflt=None):
+def get_objectForKey(self, key, dflt=None):
 	res = self.objectForKey_(key)
 	if res is None: 
 		res = dflt
 	return res
+CONVENIENCE_METHODS['objectForKey:'] = (('__getitem__', __getitem__objectForKey),
+					('has_key', has_key_objectForKey),
+					('get', get_objectForKey),
+					('__contains__', lambda self, elem: (self.objectForKey_(elem) != None)
+					 ))
 
-def __delitem__1(self, key):
-	self.removeObjectForKey_(key)
-def __setitem__1(self, key, value):
-	self.setObject_forKey_(value, key)
-def __getitem__2(self, idx):
-	res = self.objectAtIndex_(idx)
-	if res == None:
-		raise IndexError, idx
-	return res
-def __delitem__2(self, idx):
-	self.removeObjectAtIndex_(idx)
-def __setitem__2(self, idx, value):
-	self.replaceObjectAtIndex_withObject_(idx, value)
-def __hash__(self):
-	return self.hash()
-		
+CONVENIENCE_METHODS['removeObjectForKey:'] = (('__delitem__',
+					       lambda self, key: self.removeObjectForKey_( key ) ), )
 
-CONVENIENCE_METHODS['objectForKey:'] = (('__getitem__', __getitem__1), ('has_key', has_key_1), ('get', get_1))
-CONVENIENCE_METHODS['removeObjectForKey:'] = (('__delitem__', __delitem__1),)
-CONVENIENCE_METHODS['setObject:forKey:'] = (('__setitem__', __setitem__1),)
-CONVENIENCE_METHODS['count'] = (('__len__', __len__1),)
-CONVENIENCE_METHODS['description'] = (('__repr__', __repr__),)
-CONVENIENCE_METHODS['doesContain:'] = (('__contains__', __contains__),)
-CONVENIENCE_METHODS['hash'] = (('__hash__', __hash__),)
-CONVENIENCE_METHODS['isEqualTo:'] = (('__eq__', __eq__1),)
-CONVENIENCE_METHODS['isEqual:'] = (('__eq__', __eq__2),)
-CONVENIENCE_METHODS['isGreaterThan:'] = (('__gt__', __gt__),)
-CONVENIENCE_METHODS['isGreaterThanOrEqualTo:'] = (('__ge__', __ge__),)
-CONVENIENCE_METHODS['isLessThan:'] = (('__lt__', __lt__),)
-CONVENIENCE_METHODS['isLessThanOrEqualTo:'] = (('__le__', __le__),)
-CONVENIENCE_METHODS['isNotEqualTo:'] = (('__ne__', __ne__),)
-CONVENIENCE_METHODS['lenght'] = (('__len__', __len__2),)
-CONVENIENCE_METHODS['objectAtIndex:'] = (('__getitem__', __getitem__2),)
-CONVENIENCE_METHODS['removeObjectAtIndex:'] = (('__detitem__', __delitem__2),)
-CONVENIENCE_METHODS['replaceObjectAtIndex:withObject:'] = (('__setitem__', __setitem__2),)
+CONVENIENCE_METHODS['setObject:forKey:'] = (('__setitem__',
+					     lambda self, key, value: self.setObject_forKey_( value, key ) ), )
 
+CONVENIENCE_METHODS['count'] = (('__len__',
+				 lambda self: self.count() ),)
 
+CONVENIENCE_METHODS['description'] = (('__repr__',
+				       lambda self: self.description() ),)
+
+CONVENIENCE_METHODS['containsObject:'] = (('__contains__',
+					   lambda self, elem: (self.containsObject_(elem) != 0)),)
+
+CONVENIENCE_METHODS['hash'] = (('__hash__',
+				lambda self: self.hash() ),)
+
+CONVENIENCE_METHODS['isEqualTo:'] = (('__eq__',
+				      lambda self, other: self.isEqualTo_( other ) ),)
+
+CONVENIENCE_METHODS['isEqual:'] = (('__eq__',
+				    lambda self, other: self.isEqual_( other ) ),)
+
+CONVENIENCE_METHODS['isGreaterThan:'] = (('__gt__',
+					  lambda self, other: self.isGreaterThan_( other ) ),)
+
+CONVENIENCE_METHODS['isGreaterThanOrEqualTo:'] = (('__ge__',
+						   lambda self, other: self.isGreaterThanOrEqualTo_( other ) ),)
+
+CONVENIENCE_METHODS['isLessThan:'] = (('__lt__',
+				       lambda self, other: self.isLessThan_( other ) ),)
+
+CONVENIENCE_METHODS['isLessThanOrEqualTo:'] = (('__le__',
+						lambda self, other: self.isLessThanOrEqualTo_( other ) ),)
+
+CONVENIENCE_METHODS['isNotEqualTo:'] = (('__ne__',
+					 lambda self, other: self.isNotEqualTo_( other ) ),)
+
+CONVENIENCE_METHODS['length'] = (('__len__',
+				  lambda self: self.length() ),)
+
+def __getitem__objectAtIndexWithSlice(self, x, y):
+	l = len(self)
+	r = y - x
+	if r < 0:
+		return []
+	if (r - x) > l:
+		r = l - x
+	return self.subarrayWithRange_( (x, r) )
+CONVENIENCE_METHODS['objectAtIndex:'] = (('__getitem__', lambda self, index: self.objectAtIndex_( index )),
+					 ('__getslice__', __getitem__objectAtIndexWithSlice) )
+
+def __delslice__removeObjectAtIndex(self, x, y):
+	l = len(self)
+	r = y - x
+	if r < 0:
+		return
+	if (r - x) > l:
+		r = l - x
+	return self.removeObjectsInRange_( (x, r) )
+	
+CONVENIENCE_METHODS['removeObjectAtIndex:'] = (('__delitem__', lambda self, index: self.removeObjectAtIndex_( index )),
+					       ('__delslice__', __delslice__removeObjectAtIndex ) )
+
+CONVENIENCE_METHODS['replaceObjectAtIndex:withObject:'] = (('__setitem__',
+							    lambda self, index, anObject: self.replaceObjectAtIndex_withObject_( index, anObject) ),)
+
+def __setslice__replaceObjectAtIndex_withObject(self, x, y, v):
+	l = len(self)
+	r = y - x
+	if r < 0:
+		return
+	if (r - x) > l:
+		r = l - x
+	return self.replaceObjectsInRange_withObjectsFromArray_( (x, r), v )
+CONVENIENCE_METHODS['replaceObjectsInRange:withObjectsFromArray:'] = (('__setslice__', __setslice__replaceObjectAtIndex_withObject), )
 
 # Mapping protocol
 
-
-def mapping_keys(self):
-	"""
-	NSDictionary.keys()
-	"""
-	enum = self.keyEnumerator()
-	result = []
-	key = enum.nextObject()
-	while key:
-		result.append(key)
-		key = enum.nextObject()
-	return result
-
-def mapping_values(self):
-	"""
-	NSDictionary.values()
-	"""
-	enum = self.objectEnumerator()
-	result = []
-	value = enum.nextObject()
-	while value:
-		result.append(value)
-		value = enum.nextObject()
-	return result
-
 # Mappings (e.g. like dict)
 # TODO (not all are needed or even possible): 
 #   iter*, update, pop, popitem, setdefault
 # __str__ would be nice (as obj.description()),
-CONVENIENCE_METHODS['keyEnumerator'] = (('keys', mapping_keys),)
-CONVENIENCE_METHODS['objectEnumerator'] = (('values', mapping_values),)
+
+def enumeratorGenerator(anEnumerator):
+	nextObject = anEnumerator.nextObject()
+	while (nextObject):
+		yield nextObject
+		nextObject = anEnumerator.nextObject()
+
+CONVENIENCE_METHODS['keyEnumerator'] = (('keys',
+					 lambda self: self.allKeys()),
+
+					('__iter__',
+					 lambda self: enumeratorGenerator( self.keyEnumerator() ) ),
+
+					('iterkeys',
+					 lambda self: enumeratorGenerator( self.keyEnumerator() ) ) )
+
+CONVENIENCE_METHODS['objectEnumerator'] = (('values', lambda self: self.allValues()),)
 CONVENIENCE_METHODS['removeAllObjects'] = (('clear', lambda self: self.removeAllObjects()),)
 CONVENIENCE_METHODS['dictionaryWithDictionary:'] = (('copy', lambda self: type(self).dictionaryWithDictionary_(self)),)

pyobjc/Lib/objc/classnib.py

 #}
 #
 
+from Foundation import NSDictionary
 
 def parse_classes_nib(nibfile):
 	"""
 	class information.
 	"""
 	import os
-	return ClassNibParser(
-		open(os.path.join(nibfile, 'classes.nib'))).parse()
+	return NSDictionary.dictionaryWithContentsOfFile_(os.path.join(nibfile, 'classes.nib'))
 
 def generate_wrapper_module(outfp, classinfolist):
 	"""
 #
 
 import objc
-
 NSBundle = objc.lookup_class('NSBundle')
 
-class Tokenizer:
-	"""
-	A simple lexer for classes.nib files. This more or less assumes valid 
-	input, which should be pretty safe as the classes.nib is machine
-	generated.
-
-	There are two types of tokens: Some special characters and strings of
-	alphanumeric characters. There is optional whitespace between tokens.
-	"""
-
-	def __init__(self, fp):
-		self.buf = fp.read()
-
-	def nextToken(self):
-		while self.buf and self.buf[0].isspace():
-			self.buf = self.buf[1:]
-
-		if not self.buf:
-			return None
-
-		if self.buf[0] in '(){};=,':
-			res = self.buf[0]
-			self.buf = self.buf[1:]
-		else:
-			if not self.buf[0].isalnum():
-				raise ValueError, "Token error: "+self.buf[0]
-			res = ''
-			while self.buf[0] and self.buf[0].isalnum():
-				res += self.buf[0]
-				self.buf = self.buf[1:]
-		return res
-
-
-class ClassNibParser:
-	"""
-	This class is used to parse a 'classes.nib' file. It returns a python
-	datastructure that corresponds with the contents of the file.
-
-	The only public entry points in this class are the contructor and
-	the parse method.
-
-	NOTE: I (Ronald) have not yet found documentation about the format
-	of the classes.nib file. This implementation is based on reading a
-	number of classes.nib files.
-	"""
-
-	def __init__(self, fp):
-		self._tokenizer = Tokenizer(fp)
-		self._value = None
-
-	def _parse_dict(self):
-		# '{' (KEY = VALUE ';')* '}'
-		# The leading '{' is already consumed.
-
-		result = {}
-
-		token = self._tokenizer.nextToken()
-		if token == '}':
-			return res
-
-		while 1:
-			if not token[0].isalnum():
-				raise ValueError, "Bad dictionary: " + token
-			key = token
-
-			token = self._tokenizer.nextToken()
-			if token != '=':
-				raise ValueError, "Bad dictionary"
-
-			token = self._tokenizer.nextToken()
-			if token == '{':
-				value = self._parse_dict()
-			elif token == '(':
-				value = self._parse_tuple()
-			elif token[0].isalnum():
-				value = token
-			else:
-				raise ValueError, "Bad dictionary"
-
-			token = self._tokenizer.nextToken()
-			if token != ';':
-				raise ValueError, "Missing ';'"
-
-			result[key] = value
-
-			token = self._tokenizer.nextToken()
-			if token == '}':
-				break
-
-		return result
-
-	def _parse_tuple(self):
-		# '(' (VALUE ',')* ')'
-		# The leading '(' is already consumed.
-
-		result = []
-
-		token = self._tokenizer.nextToken()
-		if token == ')':
-			return tuple(result)
-
-		while 1:
-			if token == '{':
-				value = self._parse_dict()
-			elif token == '(':
-				value = self._parse_tuple()
-			else:
-				value = token
-
-			result.append(value)
-
-			token = self._tokenizer.nextToken()
-			if token == ',':
-				pass
-			elif token == ')':
-				break
-			else:
-				raise ValueError, "Missing ','"
-
-			token = self._tokenizer.nextToken()
-
-		return tuple(result)
-
-
-	def parse(self):
-		"""
-		Parse the file. Can safely be called multiple times.
-		"""
-		if not self._value:
-			token = self._tokenizer.nextToken()
-			if token != '{':
-				raise ValueError, "Not a classes.nib?"
-			self._value = self._parse_dict()
-		return self._value
-
 def _mergelists(l1, l2):
 	r = {}
 	for i in l1:
 		self._fp.write('\t"Base class for class \'%s\'"\n'%clsname)
 		if not actions and not outlets:
 			self._fp.write('\tpass\n')
-	
+
+		print outlets
 		for o in outlets:
 			self._fp.write('\t%s = IBOutlet("%s")\n'%(o, o))
 		if outlets: