Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
objc_util wishlist
-
I have found myself using
objc_util
a whole lot. Heck my pride and joy of a library is made with itSome things I wish
objc_util
had areBetter ways of responding to blocks.
A block should have it's own accessible class that functions within the block can access. Also a block should be self contained and not having to push to a global variable
class Block(object): def __init__(self): self.someValue = 1 def callOnFinish(self, passed_value): self.someValue = passed_value
Better type error handling
Currently
objc_utils
throws very cryptic errors when a type issue happens with an objc function. The type expected should be stated in the errorError objects
If something has an option that is an
NSError
a built in part of objc_util should provide that. (I currently have an implementation but it's strange)Though in all I'm still really happy with
objc_util
-
@scj643 said:
Currently objc_utils throws very cryptic errors when a type issue happens with an objc function
What exactly do you mean? I never found the
objc_util
errors to be cryptic at all. -
It doesn't tell you what type it expected
-
@scj643 because it doesn't know. The objc runtime can only get the expected type encoding of an argument or a method's return value. The type encoding specifies which primitive type an argument should be (
float
/int
/char
/etc) or whether it is an object. But since ObjC classes are basically just glorifiedstruct
s, the runtime can't differentiate between different classes. (That's why objc has theid
type to represent "any objc-object") -
It doesn't actually say what type it wants
examplev = UIView.new() v.setAlpha_('t')
-
@scj643 it says that the type error is caused by the 3rd argument (which makes sense since 1 is
self
and 2 iscmd
).
And it can't say which type it wants because it doesn't know what to expect, it just knows that what it got was wrong -
Actually, since objc_util is setting up the ctypes call (based on the method encoding), it does know the types that are needed. For instance, you can copy objc_util and make the following changes (two places, in ObjCClassMethod and ObjCinstanceMethod) where the objc_msgSend calls are:
try: res = objc_msgSend(obj.ptr, sel(self.sel_name), *args) except ctypes.ArgumentError as e: argnum=int(re.match('argument (\d*):',e.args[0]).groups()[0]) #1based e.args=('{} UserArgument #{}: Expected:{}, Got {}'.format(self.sel_name,argnum-2,argtypes[argnum-3], type(args[argnum-3])), ) raise
which produces a traceback like
>>> v = UIView.new() >>> v.setAlpha_('t') Traceback (most recent call last): File "<string>", line 1, in <module> File "/private/var/mobile/Containers/Shared/AppGroup/C534C622-2FDA-41F7-AE91-E3AAFE5FFC6B/Pythonista3/Documents/objc_util3.py", line 901, in __call__ res = objc_msgSend(obj.ptr, sel(self.sel_name), *args) ctypes.ArgumentError: setAlpha: UserArgument #1: Expected:<class 'ctypes.c_void_p'>, Got <class 'str'>
Frankly, I have never understand why the python ctypes.ArgumentError doesnt already contain that info.
-
@scj643 re: blocks, you can always add instance variables (functions are objects in python).
def myblk(blk, arg): if myblk.x: return myblk.x myblk.x=2 ... blk=ObjCBlock(myblk)
It would be nice to have a ObjCBlock as decorator:
@ObjCBlock(argtypes=[c_void_p], restype=None) def myfun(blk, someargs): ...
Also, you can have blocks that live as instance methods:
class MyDelegate(object): ... def myblock(self,_blk): ... print(self.x) ... def __init__(self): ... self.x=10 ... >>> m=MyDelegate() >>> m.blk=ObjCBlock(m.myblock,argtypes=[c_void_p]
This can let you associate other objects with a particular block, to avoid the need for globals.