Factoring out crankd's functionality

9 views
Skip to first unread message

Preston Holmes

unread,
Jul 17, 2009, 3:33:01 PM7/17/09
to pymac...@googlegroups.com
When looking through the code for crankd and trying to figure out how
it makes use of having a python callable instead of a command. I was
a bit confused by this line in the mountmanager example:

from PyMacAdmin.crankd.handlers import BaseHandler

seems that crankd is no longer part of the module?

So I was looking at the func get_callable_from_string and realized
that the callable would have to be resolvable from the python path,
not a callable I define in my running code.

I then looked a little further into how I could use crankd
notifications inside a python script. The three technologies that
crankd seems to expose are represented by:

FSEventStreamScheduleWithRunLoop

SCDynamicStoreCreateRunLoopSource

NSWorkspace.sharedWorkspace().notificationCenter()

I think there is opportunity to factor these out into 3 classes in the
PyMacAdmin module (ie):

FSEventListener

SystemConfigurationListener

WorkspaceListener

each of these would have an add_event(event, callback) method

each would have start_listening and stop_listening methods, and each
would try to clean up after itself in a __del__ method

crankd.py would then instantiate these, read the config, call the
commands etc with no change to its usage.

One thing that wasn't clear was whether the timer_callback kludge
would be needed in every case where a SystemConfigurationListener
would be use, or if that could be encapsulated.

-Preston

Chris Adams

unread,
Jul 17, 2009, 5:21:54 PM7/17/09
to pymac...@googlegroups.com
On Fri, Jul 17, 2009 at 3:33 PM, Preston Holmes <sanroq...@gmail.com> wrote:
seems that crankd is no longer part of the module?

This is actually something I've been working on - you want this branch. For some reason it didn't get pushed out even though it was supposedly merged. I'll probably have to fix it by hand but this is what you'll want:


Chris 

Chris Adams

unread,
Jul 17, 2009, 5:24:53 PM7/17/09
to pymac...@googlegroups.com
On Fri, Jul 17, 2009 at 3:33 PM, Preston Holmes <sanroq...@gmail.com> wrote:
One thing that wasn't clear was whether the timer_callback kludge
would be needed in every case where a SystemConfigurationListener
would be use, or if that could be encapsulated.

Oh, I'd also welcome this but it looks like it's a bit of a necessary evil when we're dealing with events from Cocoa. I'm pretty sure we have the same problem with the other frameworks, too, so I'd be inclined to leave this in. Otherwise, however, I'm very interested in refactoring things out - the current structure reflects a lot of hacking up until the WWDC talk last year which I never found enough time to refactor completely.

Chris 

Preston Holmes

unread,
Jul 17, 2009, 6:50:47 PM7/17/09
to pymac...@googlegroups.com

On Jul 17, 2009, at 2:24 PM, Chris Adams wrote:

>
>
> On Fri, Jul 17, 2009 at 3:33 PM, Preston Holmes <sanroq...@gmail.com
> > wrote:
> One thing that wasn't clear was whether the timer_callback kludge
> would be needed in every case where a SystemConfigurationListener
> would be use, or if that could be encapsulated.
>
> Oh, I'd also welcome this but it looks like it's a bit of a
> necessary evil when we're dealing with events from Cocoa.

I'm thinking the solution could involve threads - either python
threads or NSThreads - which have their own runloop leaving the main
run loop more responsive to signals?

Not too sure of that - but something tells me its worth a look.

I'm going to take a stab at ripping out part of crankd and put it in a
class

Another area to for listening for system events is libnotify which the
system uses for a variety of events not covered by crankd so far

http://www.dreness.com/blog/archives/65
http://svn.red-bean.com/pyobjc/trunk/notify/

Preston Holmes

unread,
Jul 21, 2009, 12:29:12 PM7/21/09
to pymac...@googlegroups.com
On Jul 17, 2009, at 2:24 PM, Chris Adams wrote:

Well I've only had a little time to poke at this, and haven't gotten anything working yet - mainly because of my lack of cocoa experience (too much of my time in this experiment was hastily scanning and trying to absorb various cocoa class references).  I'm posting this incase anyone has a tidbit that can coax me along - otherwise I'll just put this on my crowded backburner shelf for now.

The goal would be to have python code that looks like this:

def report(d):
    print d

from workspacelistener import WorkSpaceListener
listener = WorkSpaceListener()
listener.add_event("NSWorkspaceDidLaunchApplicationNotification", report)
listener.start()
while 1:
time.sleep(1)

I tried this first with a python threading - but couldn't get the requisite autoreleasepool to be recognized by the pyobjc code, but it did achieve some separation in terms of the responsiveness of the main thread to interrupts.

should be possible to use NSThread directly - because each NSThread can have its own runloop, but I'm just not there yet on the cocoa side skill/knowledge wise.  It would seem to require a new thread and runloop for each event added as once the thread is spawned there is no easy way to inject additional code.  

The other option would be to maintain the simplicity of the current code and keep everything in the main thread and just use another process and the multiprocessing module to communicate to the calling script.  This isn't in the stock leopard python, but is in 2.6 and available as a backport for 2.5 under BSD.  It has the advantage of being easier to debug than a threaded solution.

Either way a separate runloop from that associated with the python script using the tool makes the most sense to avoid the timer hack.  

from 


When configuring the run loop for a long-lived thread, it is better to add at least one input source to receive messages. Although you can enter the run loop with only a timer attached, once the timer fires, it is typically invalidated, which would then cause the run loop to exit. Attaching a repeating timer could keep the run loop running over a longer period of time, but would involve firing the timer periodically to wake your thread, which is effectively another form of polling. By contrast, an input source waits for an event to happen, keeping your thread asleep until it does.

With the NSRunloop stuff in its own thread/process the "Main" python script can just go about its regular business waiting for async callbacks, handling keyboard interrupts etc...

-Preston

Reply all
Reply to author
Forward
0 new messages