def test_object_return(self): "If a method or field returns an object, you get an instance of that type returned" Example = ObjCClass('Example') example = Example.alloc().init() Thing = ObjCClass('Thing') thing = Thing.alloc().initWithName_value_('This is thing', 2) example.thing = thing the_thing = example.thing self.assertEqual(the_thing.toString(), "This is thing 2")
def test_class_properties(self): "A Python class can have ObjC properties with synthesized getters and setters." NSURL = ObjCClass('NSURL') class URLBox(NSObject): url = objc_property(ObjCInstance) data = objc_property(ObjCInstance) @objc_method def getSchemeIfPresent(self): if self.url is not None: return self.url.scheme return None box = URLBox.alloc().init() # Default property value is None self.assertIsNone(box.url) # Assign an object via synthesized property setter and call method that uses synthesized property getter url = NSURL.alloc().initWithString_('https://www.google.com') box.url = url self.assertEqual(box.getSchemeIfPresent(), 'https') # Assign None to dealloc property and see if method returns expected None box.url = None self.assertIsNone(box.getSchemeIfPresent()) # Try composing URLs using constructors base = NSURL.URLWithString('https://pybee.org') full = NSURL.URLWithString('contributing/', relativeToURL=base) self.assertEqual( "Visit %s for details" % full.absoluteURL, "Visit https://pybee.org/contributing/ for details" ) # ObjC type conversions are performed on property assignment. box.data = "Jabberwock" self.assertEqual(box.data, "Jabberwock") Example = ObjCClass('Example') example = Example.alloc().init() box.data = example self.assertEqual(box.data, example) box.data = None self.assertIsNone(box.data)
def test_property_forcing(self): "An instance or property method can be explicitly declared as a property." Example = ObjCClass('Example') Example.declare_class_property('classMethod') Example.declare_class_property('classAmbiguous') Example.declare_property('instanceMethod') Example.declare_property('instanceAmbiguous') # A class method can be turned into a property self.assertEqual(Example.classMethod, 37) # An actual class property can be accessed as a property self.assertEqual(Example.classAmbiguous, 37) # An instance property can be accessed obj1 = Example.alloc().init() # An instance method can be turned into a property self.assertEqual(obj1.instanceMethod, 42) # An actual property can be accessed as a property self.assertEqual(obj1.instanceAmbiguous, 42) # Practical example: In Sierra, mainBundle was turned into a class property. # Previously, it was a method. NSBundle = ObjCClass('NSBundle') NSBundle.declare_class_property('mainBundle') self.assertFalse(callable(NSBundle.mainBundle), 'NSBundle.mainBundle should not be a method')
def test_enum_argument(self): "An enumerated type can be used as an argument." Example = ObjCClass('Example') obj = Example.alloc().init() self.assertEqual(obj.accessBaseIntField(), 22) self.assertEqual(obj.accessIntField(), 33) class MyEnum(Enum): value1 = 8888 value2 = 9999 value3 = 3333 value4 = 4444 obj.mutateBaseIntFieldWithValue_(MyEnum.value1) obj.mutateIntFieldWithValue_(MyEnum.value2) self.assertEqual(obj.accessBaseIntField(), MyEnum.value1.value) self.assertEqual(obj.accessIntField(), MyEnum.value2.value) obj.baseIntField = MyEnum.value3 obj.intField = MyEnum.value4 self.assertEqual(obj.accessBaseIntField(), MyEnum.value3.value) self.assertEqual(obj.accessIntField(), MyEnum.value4.value)
def test_number_return(self): "If a method or field returns a NSNumber, it is converted back to native types" Example = ObjCClass('Example') example = Example.alloc().init() self.assertEqual(example.theAnswer(), 42) self.assertAlmostEqual(example.twopi(), 2.0 * math.pi, 5)
def test_no_convert_return(self): Example = ObjCClass("Example") example = Example.alloc().init() res = example.toString(convert_result=False) self.assertNotIsInstance(res, ObjCInstance) self.assertEqual(str(ObjCInstance(res)), "This is an ObjC Example object")
def test_block_receiver_unannotated(self): BlockReceiverExample = ObjCClass("BlockReceiverExample") instance = BlockReceiverExample.alloc().init() def block(a, b): return a + b with self.assertRaises(ValueError): instance.receiverMethod_(block)
def test_decimal_method(self): "A method with a NSDecimalNumber arguments can be handled." Example = ObjCClass('Example') example = Example.alloc().init() result = example.areaOfTriangleWithWidth_andHeight_(Decimal('3.0'), Decimal('4.0')) self.assertEqual(result, Decimal('6.0')) self.assertIsInstance(result, Decimal, 'Result should be a Decimal')
def test_struct_return_send(self): "Methods returning structs of different sizes by value can be handled when using send_message." Example = ObjCClass('Example') example = Example.alloc().init() self.assertEqual(send_message(example, "intSizedStruct", restype=struct_int_sized).x, b"abc") self.assertEqual(send_message(example, "oddlySizedStruct", restype=struct_oddly_sized).x, b"abcd") self.assertEqual(send_message(example, "largeStruct", restype=struct_large).x, b"abcdefghijklmnop")
def test_argument(self): Example = ObjCClass("Example") example = Example.alloc().init() a = self.make_array(self.py_list) # Call a method with an NSArray instance self.assertEqual(example.processArray(a), 'two') # Call the same method with the Python list self.assertEqual(example.processArray(self.py_list), 'two')
def test_block_round_trip(self): BlockRoundTrip = ObjCClass("BlockRoundTrip") instance = BlockRoundTrip.alloc().init() def block(a: int, b: int) -> int: return a + b returned_block = instance.roundTrip_(block) self.assertEqual(returned_block(8, 9), 17)
def test_property(self): Example = ObjCClass("Example") example = Example.alloc().init() a = self.make_array(self.py_list) example.array = a self.assertEqual(example.array, self.py_list) self.assertIsInstance(example.array, ObjCListInstance) self.assertEqual(example.array[1], 'two')
def test_block_receiver_explicit(self): BlockReceiverExample = ObjCClass("BlockReceiverExample") instance = BlockReceiverExample.alloc().init() values = [] block = Block(lambda a, b: values.append(a + b), None, int, int) instance.receiverMethod_(block) self.assertEqual(values, [27])
def test_block_delegate_auto(self): class DelegateAuto(NSObject): @objc_method def exampleMethod_(self, block: objc_block): block(4, 5) BlockObjectExample = ObjCClass("BlockObjectExample") delegate = DelegateAuto.alloc().init() instance = BlockObjectExample.alloc().initWithDelegate_(delegate) result = instance.blockExample() self.assertEqual(result, 9)
def test_block_delegate_method_manual_pytypes(self): class DelegateManualPY(NSObject): @objc_method def exampleMethod_(self, block): ObjCBlock(block, None, int, int)(2, 3) BlockObjectExample = ObjCClass("BlockObjectExample") delegate = DelegateManualPY.alloc().init() instance = BlockObjectExample.alloc().initWithDelegate_(delegate) result = instance.blockExample() self.assertEqual(result, 5)
def test_property(self): Example = ObjCClass("Example") example = Example.alloc().init() d = self.make_dictionary(self.py_dict) example.dict = d self.assertEqual(example.dict, self.py_dict) self.assertIsInstance(example.dict, ObjCDictInstance) self.assertEqual(example.dict['one'], 'ONE')
def test_polymorphic_constructor(self): "Check that the right constructor is activated based on arguments used" Example = ObjCClass('Example') obj1 = Example.alloc().init() obj2 = Example.alloc().initWithIntValue_(2242) obj3 = Example.alloc().initWithBaseIntValue_intValue_(3342, 3337) self.assertEqual(obj1.baseIntField, 22) self.assertEqual(obj1.intField, 33) self.assertEqual(obj2.baseIntField, 44) self.assertEqual(obj2.intField, 2242) self.assertEqual(obj3.baseIntField, 3342) self.assertEqual(obj3.intField, 3337) # Protected constructors can't be invoked with self.assertRaises(AttributeError): Example.alloc().initWithString_("Hello")
def test_static_access_non_static(self): "An instance field/method cannot be accessed from the static context" Example = ObjCClass('Example') obj = Example.alloc().init() with self.assertRaises(AttributeError): obj.staticIntField with self.assertRaises(AttributeError): obj.get_staticIntField()
def test_block_receiver(self): BlockReceiverExample = ObjCClass("BlockReceiverExample") instance = BlockReceiverExample.alloc().init() values = [] def block(a: int, b: int) -> None: values.append(a + b) instance.receiverMethod_(block) self.assertEqual(values, [27])
def test_double_method_send(self): "A method with a double argument can be handled by send_message." Example = ObjCClass('Example') example = Example.alloc().init() self.assertAlmostEqual( send_message( example, "areaOfCircle:", 1.5, restype=c_double, argtypes=[c_double] ), 1.5 * math.pi, 5 )
def test_non_existent_method(self): "An attribute error is raised if you invoke a non-existent method." Example = ObjCClass('Example') obj1 = Example.alloc().init() # Non-existent methods raise an error. with self.assertRaises(AttributeError): obj1.method_doesnt_exist() # Cache warming doesn't affect anything. with self.assertRaises(AttributeError): obj1.method_doesnt_exist()
def test_struct_return(self): "Methods returning structs of different sizes by value can be handled." Example = ObjCClass('Example') example = Example.alloc().init() types.register_encoding(b'{int_sized=[4c]}', struct_int_sized) self.assertEqual(example.intSizedStruct().x, b"abc") types.register_encoding(b'{oddly_sized=[5c]}', struct_oddly_sized) self.assertEqual(example.oddlySizedStruct().x, b"abcd") types.register_encoding(b'{large=[17c]}', struct_large) self.assertEqual(example.largeStruct().x, b"abcdefghijklmnop")
def test_multitype_list_property(self): class MultitypeListContainer(NSObject): data = objc_property() Example = ObjCClass('Example') example = Example.alloc().init() # All types can be stored in a list. obj = MultitypeListContainer.alloc().init() obj.data = [4, True, 'Hello', example] self.assertEqual(obj.data, [4, True, 'Hello', example]) self.assertIsInstance(obj.data, ObjCListInstance)
def test_method_send(self): "An instance method can be invoked with send_message." Example = ObjCClass('Example') obj = Example.alloc().init() self.assertEqual(send_message(obj, "accessBaseIntField", restype=c_int), 22) self.assertEqual(send_message(obj, "accessIntField", restype=c_int), 33) send_message(obj, "mutateBaseIntFieldWithValue:", 8888, restype=None, argtypes=[c_int]) send_message(obj, "mutateIntFieldWithValue:", 9999, restype=None, argtypes=[c_int]) self.assertEqual(send_message(obj, "accessBaseIntField", restype=c_int), 8888) self.assertEqual(send_message(obj, "accessIntField", restype=c_int), 9999)
def test_method(self): "An instance method can be invoked." Example = ObjCClass('Example') obj = Example.alloc().init() self.assertEqual(obj.accessBaseIntField(), 22) self.assertEqual(obj.accessIntField(), 33) obj.mutateBaseIntFieldWithValue_(8888) obj.mutateIntFieldWithValue_(9999) self.assertEqual(obj.accessBaseIntField(), 8888) self.assertEqual(obj.accessIntField(), 9999)
def test_field(self): "A field on an instance can be accessed and mutated" Example = ObjCClass('Example') obj = Example.alloc().init() self.assertEqual(obj.baseIntField, 22) self.assertEqual(obj.intField, 33) obj.baseIntField = 8888 obj.intField = 9999 self.assertEqual(obj.baseIntField, 8888) self.assertEqual(obj.intField, 9999)
def test_block_delegate_auto_struct(self): class BlockStruct(Structure): _fields_ = [ ('a', c_int), ('b', c_int), ] class DelegateAutoStruct(NSObject): @objc_method def structBlockMethod_(self, block: objc_block) -> int: return block(BlockStruct(42, 43)) BlockObjectExample = ObjCClass("BlockObjectExample") delegate = DelegateAutoStruct.alloc().init() instance = BlockObjectExample.alloc().initWithDelegate_(delegate) result = instance.structBlockExample() self.assertEqual(result, 85)
def test_argument(self): Example = ObjCClass("Example") example = Example.alloc().init() d = self.make_dictionary(self.py_dict) # Call a method with an NSDictionary instance self.assertIsNone(example.processDictionary(d)) # Call the same method with the raw Python dictionary self.assertIsNone(example.processDictionary(self.py_dict)) raw = {'data': 'stuff', 'other': 'gadgets'} d = self.make_dictionary(raw) # Call a method with an NSDictionary instance self.assertEqual(example.processDictionary(d), 'stuff') # Call the same method with the raw Python dictionary self.assertEqual(example.processDictionary(raw), 'stuff')
def test_mutator_like_method(self): "A method that looks like a mutator doesn't confuse issues." Example = ObjCClass('Example') obj1 = Example.alloc().init() # setSpecialValue: looks like it might be a mutator # for a specialValue property, but this property doesn't exist. # We can invoke the method directly... obj1.setSpecialValue_(42) # ... but retrieving like a property is an error: with self.assertRaises(AttributeError): obj1.specialValue # ... and mutating like a property is an error: with self.assertRaises(AttributeError): obj1.specialValue = 37
def test_mutator_like_method(self): "A method that looks like a mutator doesn't confuse issues." Example = ObjCClass('Example') obj1 = Example.alloc().init() # setSpecialValue: looks like it might be a mutator # for a specialValue property, but this property doesn't exist. # We can invoke the method directly... obj1.setSpecialValue_(42) # ... but retrieving like a property is an error with self.assertRaises(AttributeError): obj1.specialValue # ...until you set it explicitly... obj1.specialValue = 37 # ...at which point it's fair game to be retrieved. self.assertEqual(obj1.specialValue, 37)
def test_string_return(self): "If a method or field returns a string, you get a Python string back" Example = ObjCClass('Example') example = Example.alloc().init() self.assertEqual(example.toString(), "This is an ObjC Example object")
def test_constant_string_return(self): "If a method or field returns a *constant* string, you get a Python string back" Example = ObjCClass('Example') example = Example.alloc().init() self.assertEqual(example.smiley(), "%-)")
""" Save values on disk This module makes possible to save values on disk. Values are shared between the Today Widget and the main app. Values are stored in a JSON dictionary, so it's not possible to save every type of data. """ from rubicon.objc import ObjCClass import json NSUserDefaults = ObjCClass("NSUserDefaults") userDefaults = NSUserDefaults.alloc().initWithSuiteName("group.pyto") if userDefaults.valueForKey("userKeys") is None: userDefaults.setValue("{}", forKey="userKeys") def __dictionary__(): return json.loads(str(userDefaults.valueForKey("userKeys"))) def get(key: str): """ Returns the value stored with the given key. :param key: The key identifying the value. """ return __dictionary__()[key]
def test_float_method(self): "A method with a float argument can be handled." Example = ObjCClass('Example') example = Example.alloc().init() self.assertEqual(example.areaOfSquare_(1.5), 2.25)
def test_float_method_send(self): "A method with a float argument can be handled by send_message." Example = ObjCClass('Example') example = Example.alloc().init() self.assertEqual(send_message(example, "areaOfSquare:", 1.5, restype=c_float, argtypes=[c_float]), 2.25)
def test_interface(self): "An ObjC protocol implementation can be defined in Python." results = {} NSObject = ObjCClass('NSObject') class Handler(NSObject): @objc_method def initWithValue_(self, value: int): self.value = value return self @objc_method def peek_withValue_(self, example, value: int) -> None: results['string'] = example.toString() + " peeked" results['int'] = value + self.value @objc_method def poke_withValue_(self, example, value: int) -> None: results['string'] = example.toString() + " poked" results['int'] = value + self.value @objc_method def reverse_(self, input): return ''.join(reversed(input)) @objc_method def message(self): return "Alea iacta est." @objc_classmethod def fiddle_(cls, value: int) -> None: results['string'] = "Fiddled with it" results['int'] = value # Create two handler instances so we can check the right one # is being invoked. handler1 = Handler.alloc().initWithValue_(5) handler2 = Handler.alloc().initWithValue_(10) # Create an Example object, and register a handler with it. Example = ObjCClass('Example') example = Example.alloc().init() example.callback = handler2 # Check some Python-side attributes self.assertEqual(handler1.value, 5) self.assertEqual(handler2.value, 10) # Invoke the callback; check that the results have been peeked as expected example.testPeek_(42) self.assertEqual(results['string'], 'This is an ObjC Example object peeked') self.assertEqual(results['int'], 52) example.testPoke_(37) self.assertEqual(results['string'], 'This is an ObjC Example object poked') self.assertEqual(results['int'], 47) self.assertEqual(example.getMessage(), 'Alea iacta est.') self.assertEqual(example.reverseIt_('Alea iacta est.'), '.tse atcai aelA') Handler.fiddle_(99) self.assertEqual(results['string'], 'Fiddled with it') self.assertEqual(results['int'], 99)
def test_block_receiver_lambda(self): BlockReceiverExample = ObjCClass("BlockReceiverExample") instance = BlockReceiverExample.alloc().init() with self.assertRaises(ValueError): instance.receiverMethod_(lambda a, b: a + b)
def test_double_method_send(self): "A method with a double argument can be handled by send_message." Example = ObjCClass('Example') example = Example.alloc().init() self.assertAlmostEqual(send_message(example, "areaOfCircle:", 1.5, restype=c_double, argtypes=[c_double]), 1.5 * math.pi, 5)
def test_interface_return_struct(self): "An ObjC protocol implementation that returns values by struct can be defined in Python." results = {} Thing = ObjCClass("Thing") class StructReturnHandler(Thing): @objc_method def initWithValue_(self, value): self.value = py_from_ns(value) return self @objc_method def computeSize_(self, input: NSSize) -> NSSize: results['size'] = True sup = send_super(__class__, self, 'computeSize:', input, restype=NSSize, argtypes=[NSSize]) return NSSize(input.width + self.value, sup.height) @objc_method def computeRect_(self, input: NSRect) -> NSRect: results['rect'] = True sup = send_super(__class__, self, 'computeRect:', input, restype=NSRect, argtypes=[NSRect]) return NSMakeRect( input.origin.y + self.value, sup.origin.x, input.size.height + self.value, sup.size.width ) # Register a second method returning NSSize. Don't # have to use it - just have to register that it exists. @objc_method def origin(self) -> NSSize: return NSSize(0, 0) # Create two handler instances so we can check the right one # is being invoked. handler1 = StructReturnHandler.alloc().initWithValue_(5) handler2 = StructReturnHandler.alloc().initWithValue_(10) outSize = handler1.computeSize(NSSize(20, 30)) self.assertEqual(outSize.width, 25) self.assertEqual(outSize.height, 90) self.assertTrue(results.get('size')) outRect = handler2.computeRect(NSMakeRect(10, 20, 30, 40)) self.assertEqual(outRect.origin.x, 30) self.assertEqual(outRect.origin.y, 110) self.assertEqual(outRect.size.width, 50) self.assertEqual(outRect.size.height, 60) self.assertTrue(results.get('rect')) # Invoke a method through an interface. Example = ObjCClass("Example") obj = Example.alloc().init() # Test the base class directly thing1 = Thing.alloc().init() obj.thing = thing1 outSize = obj.testThing(10) self.assertEqual(outSize.width, 0) self.assertEqual(outSize.height, 30) # Test the python handler obj.thing = handler1 outSize = obj.testThing(15) self.assertEqual(outSize.width, 5) self.assertEqual(outSize.height, 45)
def test_string_argument(self): "A method with a string argument can be passed." Example = ObjCClass('Example') example = Example.alloc().init() self.assertEqual(example.duplicateString_("Wagga"), "WaggaWagga")
def test_block_property_pytypes(self): BlockPropertyExample = ObjCClass("BlockPropertyExample") instance = BlockPropertyExample.alloc().init() result = ObjCBlock(instance.blockProperty, int, int, int)(1, 2) self.assertEqual(result, 3)
def test_double_method(self): "A method with a double argument can be handled." Example = ObjCClass('Example') example = Example.alloc().init() self.assertAlmostEqual(example.areaOfCircle_(1.5), 1.5 * math.pi, 5)