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.
Prototyping NSKeyValueBindingCreation in Python
May 5, 2009
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)
map() that supports deferreds
April 28, 2009
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)
Python bindings for BWToolkit
April 28, 2009
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:
- It find the base class init method
- Invokes it and checks the result of nil
- Invokes the decorated method with the new instance
- 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.