NatMap 0.0

May 21, 2009

These days everyone got a “home router” of sort, and these either support UPnP IGD (Microsoftish) or NAT-PMP (Appleish). I’ve been looking for a good NAT mapping library for Python for a while now, without finding one that fits my needs:

  • Uses Twisted 2.5
  • No big dependencies (No SOAP library, no large-ass UPnP stack)
  • Neat in general

There’s nattraverso which provides UPnP mapping and is based on Twisted. But it requires a rather large SOAP library. For NAT-PMP there’s a few hacks (notably this) around, but nothing really serious.

I there for went a head of hacked up something I called NatMap; a small NAT mapping library. It requires Twisted 2.5 and Python 2.5. That’s it. If someone give me an incentive I might even refactor parts so that it works with Python 2.3 and 2.4 (patches are welcome).

The source can be found at github, where you also can download version 0.0. It has been developed on MacOS X 1.5.6 and only tested there. There is a small example included in the tarball.

Currently it only supportsUPnP, because I only own UPnP devices at this stage. When I get my shit together I’ll pick up a AirPort and implement NAT-PMP.

Comments are more than welcome.

The NSKeyValueBindingCreation informal protocol is about allowing the user to establish bindings between properties of the receiver and an object. The protocol is normally implemented by controllers such as NSArrayController and NSControl subclasses.

In the application I’m writing I needed to implement the protocol so I get notifications when the user make changes (via my Preferences window) to my Account model object.  These changes should of course take effect directly.

First I started to implement the protocol as a mixin class, but that didn’t work because of PyObjC reasons I really don’t know about. So I turned to the composite pattern and implemented all the functionaly in a support class instead. Classes that want to expose bindings should implement the bind:toObject:withKeyPath:options: and observeValueForKeyPath:ofObject:change:context: selectors like this:

class Foo(NSObject):
    bindingSupport = None

    def bind_toObject_withKeyPath_options_(self, binding, anObject, keyPath, options):
        if self.bindingSupport is None:
            self.bindingSupport = KeyValueBindingSupport(self)
        self.bindingSupport.bind(binding, anObject, keyPath, options)

    def observeValueForKeyPath_ofObject_change_context_(self, keyPath, anObject,
                                                         change, context):
	self.bindingSupport.observe(keyPath, anObject, change)

Simple enough? KeyValueBindingSupport also has a method to realize all bindings, useful when all bindings are setup and you want all values to be propagated at once.

class KeyValueBindingSupport:
    """
    Class that helps the user to implement the
    NSKeyValueBindingCreation protocol.
    """

    def __init__(self, instance):
	self.bindings = {}
	self.instance = instance

    def bind(self, binding, toObject, keyPath, options, realize=True):
	"""
        Establishes binding between the given property C{binding} and
        the property of the given object specified by C{keyPath}.                                                                                     

        @param realize: True if the binding should be realized now.
        """
        toObject.addObserver_forKeyPath_options_context_(self.instance, keyPath, 0, None)
        self.bindings[(toObject, keyPath)] = binding
	if realize:
            self.observe(keyPath, toObject, {})

    def unbind(self, binding):
	bindings = list()
        for (object, keypath), value in self.bindings.iteritems():
            if value == binding:
		bindings.append((object, keypath))
	for object, keypath in bindings:
            del self.bindings[(object, keypath)]

    def realize(self):
        """
        Realize all bindings.
        """
        for object, keypath in self.bindings:
            self.observe(keyPath, object, {})

    def observe(self, keyPath, object, change):
	try:
            binding = self.bindings[(object, keyPath)]
	except IndexError:
            return
	self.instance.setValue_forKey_(object.valueForKey_(keyPath), binding)

(If you find any problems, please let me know)

I asked on #twisted about an implementation of map() that supports the function to return a deferred.  exarkun (of course) had an example of such.  So here’s for me and google to remember:

from twisted.internet import defer,

@defer.inlineCallbacks
def map(fn, *iterables):
    """
    Implementation of the built-in C{map} function that also supports
    that the passed function C{fn} can return a L{Deferred}.
    """
    if len(iterables) == 1:
        result = list()
        for element in iterables[0]:
            mapped = yield fn(element)
            result.append(mapped)
        defer.returnValue(tuple(result))
    else:
        result = list()
        for iterable in iterables:
            r = list()
            for element in iterable:
                mapped = yield fn(element)
                r.append(mapped)
            result.append(tuple(r))
        defer.returnValue(result)

Updated: A few related articles: Here and here.

Interface Builder and Cocoa lack a lot these juicy controls that we see in applications such as iTunes or Mail.  This (I guess) drove Brandon Walkin to create BWToolkit.  The great thing about it, except for the excellent controls, is that it comes with a Interface Builder plugin.

In my quest to make my Python hacking under OS X easier I crafted some bindings for the framework that simply loads the bundle.  This is enough to get basic functionality working.

You can get the stuff from github. Please feel free to use as you like, provided that you send me patches if you find anything wrong or not working. Just run “python setup.py install” and you should be able to do “import BWToolkitFramework” in your main.py file.

Simplify init-methods

April 27, 2009

The last month or so I’ve experimented with PyObjC, since I have the python knowledge already but would like to gain some more about Cocoa and Mac OS X.

Anyhow, what I’ve found cumbersome is to write the standard init-methods.  I’m not the first to get annoyed by this.  But instead of wining I decided to hack up a simple method decorator:

Like Jim, I tend to forget to return self from the method, and my own classes very seldom (as in never) changes self to something else, so there’s really no need for it.

Here’s the code:

def initWithSuper(original):
    """
    Method decorator to simply writing init-method for PyObjC classes.                                                                               

    This decorator eliminates the need to call [super init] and check
    the result of None.  Plus you do not have to remember to return
    'self', since that is already done for you.
    """
    def method(self, *args):
        initMethod = self.__class__.__bases__[0].init
        self = initMethod(self)
        if self is None:
            return None
        original(self, *args)
        return self
    return method

To break down the code:

  1. It find the base class init method
  2. Invokes it and checks the result of nil
  3. Invokes the decorated method with the new instance
  4. Returns the new instance

Simple enough?

But what about when you want to write something a bit more complex:

def initWithSelector(superInitMethod, passArgs=True):
    """
    Method decorator to simply how you write PyObjC init-methods.                                                                                    

    This decorator eliminates the need to call [super init] and check
    the result of None.  Plus you do not have to remember to return
    'self', since that is already done for you.                                                                                                      

    This decorator is a bit more feature rich thatn initWithSuper in
    that it allows the class writer to specify what selector to invoke.                                                                              

    Example usage:
    {{{
        class MyController(NSWindowController):
            ivar = objc.ivar('ivar')                                                                                                                 

            @initWithSuper(NSObject.initWithWindow_)
            def initWithMyOwnLittleWindow_(self, window):
                self.ivar = window
    }}}
    """
    def decorator(fn):
        def initMethod(self, *args):
            if passArgs:
                self = superInitMethod(self, *args)
            else:
                self = superInitMethod(self)
            if self is None:
                return None
            fn(self, *args)
            return self
        return initMethod
    return decorator

Some approach but you have to specify parent selector,  also arguments may be omitted if passArgs is set to False.

So, I took a few minutes and reworked Matt’s nice looking scope bar into a little framework. While at it I turned the formal protocol into two informal protocols; MGScopeBarDataSource and MGScopeBarDelegate.

But the fun part for me was to write PyObj-C bindings for it, so now I can use it in my own applications.  I’m still amazed how easy it is to wrap a Objective-C library so that it can be used in Python.  Enough, here’s the result: ScopeBar.  Just build the framework in Xcode and install in /System/Library/Frameworks.

To install the bindings, use the setup.py file as usual.  The Python example has a setup-file of its own; ScopeBarExample_setup.py, to be used with py2app.

Follow

Get every new post delivered to your Inbox.