<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><description>open source software hacker • cofounder of Unstoppable Rocket • web application superhero
www: brianbeck.com
email: exogen@gmail.com</description><title>Brian Beck's Text Adventure</title><generator>Tumblr (3.0; @exogen)</generator><link>http://blog.brianbeck.com/</link><item><title>
  “The Cleveland Tourism Board gave me 14 million dollars...</title><description>&lt;object width="400" height="336"&gt;&lt;param name="movie" value="http://www.youtube.com/v/ysmLA5TqbIY&amp;rel=0&amp;egm=0&amp;showinfo=0&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/ysmLA5TqbIY&amp;rel=0&amp;egm=0&amp;showinfo=0&amp;fs=1" type="application/x-shockwave-flash" width="400" height="336" allowFullScreen="true" wmode="transparent"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br/&gt;&lt;br/&gt;&lt;blockquote&gt;
  &lt;p&gt;“The Cleveland Tourism Board gave me 14 million dollars about 8 months ago to make a promotional video to bring people to Cleveland. As usual, I waited till the last minute and I ended up having to shoot and edit it in about an hour yesterday afternoon.” — &lt;a href="http://www.youtube.com/user/bishopvids"&gt;bishopvids&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><link>http://blog.brianbeck.com/post/97196132</link><guid>http://blog.brianbeck.com/post/97196132</guid><pubDate>Fri, 17 Apr 2009 10:55:19 -0400</pubDate></item><item><title>Cleveland Code Co-op meeting on Sunday</title><description>&lt;p&gt;The fifth meeting of the &lt;a href="http://nooss.org/wiki/Cleveland_Code_Co-op"&gt;Cleveland Code Co-op&lt;/a&gt; will commence this
Sunday, February 22, from 13:00 till 19:00.  We’re expecting more
participants than usual, and likely projects so far include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://github.com/jleedev/redit/"&gt;redit&lt;/a&gt;, a text editor in Ruby&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://code.google.com/p/cwru-hackers/"&gt;80sheep&lt;/a&gt;, an ADC (peer-to-peer) client in Python&lt;/li&gt;
&lt;li&gt;a Python tutorial for beginners&lt;/li&gt;
&lt;li&gt;your wildest software fantasies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This month’s meeting will take place in the EECS student lounge at
Case Western Reserve University, which is located in the Glennan
Building.  &lt;a href="http://www.nooss.org/wiki/Cleveland_Code_Co-op/CWRU_Location"&gt;Directions are located on the wiki&lt;/a&gt;.  You can also join us on IRC in &lt;a href="irc://irc.freenode.net#c3"&gt;#C3 on irc.freenode.net&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Food and drinks will be provided!  Hope to see you there.&lt;/p&gt;</description><link>http://blog.brianbeck.com/post/79756939</link><guid>http://blog.brianbeck.com/post/79756939</guid><pubDate>Thu, 19 Feb 2009 15:55:53 -0500</pubDate></item><item><title>Python instance descriptors: when class descriptors aren't dynamic enough</title><description>&lt;p&gt;&lt;a href="http://users.rcn.com/python/download/Descriptor.htm"&gt;Python descriptors&lt;/a&gt; are great for customizing access to attributes on a class or instance.  They are a big win for tasks like mapping Python objects to data from non-Python sources (such as SQL), since mapped attributes will need to be encoded/decoded and connected to other attributes in some way.&lt;/p&gt;

&lt;p&gt;Below is a very simple descriptor; as you can see, accessing it from &lt;em&gt;both the class and the instance&lt;/em&gt; invoke the descriptor protocol:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Test(object):
    pass

class Descriptor(object):
    def __get__(self, instance, owner):
        return "Hello, world."

&gt;&gt;&gt; Test.x = Descriptor()
&gt;&gt;&gt; Test.x
'Hello, world.'
&gt;&gt;&gt; test = Test()
&gt;&gt;&gt; test.x
'Hello, world.'
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, in order to add descriptors to an object, they must be added to the object’s class.  Descriptors added to an instance do not invoke the descriptor protocol:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&gt;&gt;&gt; test.y = Descriptor()
&gt;&gt;&gt; test.y
&lt;__main__.Descriptor object at 0x16fe810&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This means that creating an instance with dynamic (determined at runtime) descriptors requires either the heavy-handed approach of generating a class just for that object (since adding descriptors to its class will add them to all other instances of the class), or the ad-hoc approach of redefining &lt;code&gt;getattr&lt;/code&gt;/&lt;code&gt;setattr&lt;/code&gt; behavior (essentially re-implementing your own descriptor protocol).&lt;/p&gt;

&lt;p&gt;It turns out the latter approach is not as messy as it first sounds.  Below is a class that enables “instance descriptors”:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class InstanceDescriptorMixin(object):
    def __getattribute__(self, name):
        value = object.__getattribute__(self, name)
        if hasattr(value, '__get__'):
            value = value.__get__(self, self.__class__)
        return value

    def __setattr__(self, name, value):
        try:
            obj = object.__getattribute__(self, name)
        except AttributeError:
            pass
        else:
            if hasattr(obj, '__set__'):
                return obj.__set__(self, value)
        return object.__setattr__(self, name, value)

class Test(InstanceDescriptorMixin):
    pass

&gt;&gt;&gt; test = Test()
&gt;&gt;&gt; test.z = Descriptor()
&gt;&gt;&gt; test.z
'Hello, world.'
&lt;/code&gt;&lt;/pre&gt;</description><link>http://blog.brianbeck.com/post/74086029</link><guid>http://blog.brianbeck.com/post/74086029</guid><pubDate>Thu, 29 Jan 2009 12:41:00 -0500</pubDate><category>python</category></item><item><title>"The purpose of syntax highlighting is to turn your code into a map, not The Jimi Hendrix Experience."</title><description>“The purpose of syntax highlighting is to turn your code into a map, not The Jimi Hendrix Experience.”</description><link>http://blog.brianbeck.com/post/68776504</link><guid>http://blog.brianbeck.com/post/68776504</guid><pubDate>Tue, 06 Jan 2009 15:29:26 -0500</pubDate></item><item><title>geopy sprint at December C³ meeting</title><description>Today (Sunday) the third meeting of the &lt;a href="http://nooss.org/wiki/Cleveland_Code_Co-op"&gt;Cleveland Code Co-op&lt;/a&gt; will be held from 1pm to 7pm at &lt;a href="http://www.gypsybeans.com/"&gt;Gypsy Beans &amp; Bakery&lt;/a&gt;.  We’ll be focusing on &lt;a href="http://exogen.case.edu/projects/geopy/"&gt;geopy&lt;/a&gt; again, continuing our sprint goals from &lt;a href="http://blog.brianbeck.com/post/57991307/geopy-sprint"&gt;last time&lt;/a&gt;.  All are welcome to attend.  You can join us via IRC in &lt;a href="irc://irc.freenode.net#c3"&gt;#c3 on irc.freenode.net&lt;/a&gt;.</description><link>http://blog.brianbeck.com/post/64744298</link><guid>http://blog.brianbeck.com/post/64744298</guid><pubDate>Sat, 13 Dec 2008 23:51:15 -0500</pubDate></item><item><title>Christmas tree adventure</title><description>&lt;p&gt;I’ve never taken the time to get a Christmas tree while living on my own before because, honestly, Christmas doesn’t mean much to me.  But having moved into a spacious single-family carriage house with Steve, it sounded a lot more fun to drag a big needly tree into my living room this year.  Today Mandy and I drove all the way out to &lt;a href="http://www.tower-n-pines.com/"&gt;Tower-N-Pines Farm&lt;/a&gt; to cut down our own tree.  We followed this up with dinner at Mary Yoder’s Amish Kitchen, where Mandy told me everything she knows about the Amish (Middlefield has the world’s fourth largest Amish population).&lt;/p&gt;

&lt;p&gt;I was hoping for an axe, but they only provided hacksaws.  I then realized that, despite being cooler and more fun, an axe would have required blindly swinging your arms into the branches:
&lt;img src="http://media.brianbeck.com/images/tree/brian.jpg" alt="Brian holding a hacksaw"/&gt;&lt;/p&gt;

&lt;p&gt;We did a &lt;em&gt;lot&lt;/em&gt; of walking and probably looked at every single tree.  This took a long time:
&lt;img src="http://media.brianbeck.com/images/tree/walking.jpg" alt="Brian walking among the trees"/&gt;&lt;/p&gt;

&lt;p&gt;We settled on this one:
&lt;img src="http://media.brianbeck.com/images/tree/precut.jpg" alt="Brian crawling under the tree"/&gt;&lt;/p&gt;

&lt;p&gt;Finally, the moment I had been waiting for:
&lt;img src="http://media.brianbeck.com/images/tree/moment.jpg" alt="Brian cheerily cutting the tree"/&gt;&lt;/p&gt;

&lt;p&gt;Mandy even got her chance to make this face:
&lt;img src="http://media.brianbeck.com/images/tree/fierce.jpg" alt="Mandy making a fierce face with hacksaw"/&gt;&lt;/p&gt;

&lt;p&gt;It worked:
&lt;img src="http://media.brianbeck.com/images/tree/fell.jpg" alt="Brian felling the tree"/&gt;&lt;/p&gt;

&lt;p&gt;Note all the inferior trees in the background:
&lt;img src="http://media.brianbeck.com/images/tree/mandy.jpg" alt="Mandy holding the felled tree"/&gt;&lt;/p&gt;

&lt;p&gt;Then there was dragging:
&lt;img src="http://media.brianbeck.com/images/tree/drag.jpg" alt="Brian dragging the tree"/&gt;&lt;/p&gt;

&lt;p&gt;We tied it to the roof of Mandy’s car using the rope from my grappling hook (farm’s ropes? $11).  It arrived safely:
&lt;img src="http://media.brianbeck.com/images/tree/unwrap.jpg" alt="Brian preparing the tree"/&gt;&lt;/p&gt;

&lt;p&gt;Steve made up for his non-participation by helping “unfell” the tree:
&lt;img src="http://media.brianbeck.com/images/tree/steve.jpg" alt="Steve holding the tree at home"/&gt;&lt;/p&gt;

&lt;p&gt;Real mature, Steve:
&lt;img src="http://media.brianbeck.com/images/tree/adjusting.jpg" alt="Steve pretending to hit Brian with a hammer"/&gt;&lt;/p&gt;

&lt;p&gt;Steve guesstimates that the tree is “almost 10 feet” tall.
&lt;img src="http://media.brianbeck.com/images/tree/done.jpg" alt="Steve and Brian admiring their tree"/&gt;&lt;/p&gt;</description><link>http://blog.brianbeck.com/post/64738729</link><guid>http://blog.brianbeck.com/post/64738729</guid><pubDate>Sat, 13 Dec 2008 22:59:00 -0500</pubDate></item><item><title>Roomba decided to take its docking station — originally...</title><description>&lt;img src="http://4.media.tumblr.com/MzaUlGORah0mgz1tg4PWBjtDo1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;Roomba decided to take its docking station — originally placed in a much more sensible location — for a walk.</description><link>http://blog.brianbeck.com/post/62683797</link><guid>http://blog.brianbeck.com/post/62683797</guid><pubDate>Tue, 02 Dec 2008 16:44:03 -0500</pubDate></item><item><title>geopy sprint at November C³ meeting</title><description>&lt;p&gt;After suffering from over a year of poor maintenance, &lt;a href="http://exogen.case.edu/projects/geopy/"&gt;geopy&lt;/a&gt; is finally
getting some love this month.  A few other developers and I will be
focusing on geopy at this month’s &lt;a href="http://nooss.org/wiki/Cleveland_Code_Co-op"&gt;Cleveland Code Co-op&lt;/a&gt; meeting.
We’ve come up with an ambitious todo list, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merging pending patches (bug fixes, Python 2.3 support, accuracy
support)&lt;/li&gt;
&lt;li&gt;Adding unit tests&lt;/li&gt;
&lt;li&gt;Reverse geocoding support (finding locations near a point)&lt;/li&gt;
&lt;li&gt;Higher level Points and Locations (instead of tuples and strings)&lt;/li&gt;
&lt;li&gt;Keeping up with third-party geocoder APIs (and hacks)&lt;/li&gt;
&lt;li&gt;A “compound” geocoder for querying multiple geocoders (as
fallbacks or for averaging results)&lt;/li&gt;
&lt;li&gt;A parser module with support for geotagged documents (including the
Geo microformat), ISO 6709, GPX files, etc.&lt;/li&gt;
&lt;li&gt;Geohash encoding/decoding&lt;/li&gt;
&lt;li&gt;A formatter module for pretty-printing coordinates, distances, and
ordinal directions (think “south by southwest”)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setuptools&lt;/code&gt; entry points to support geocoder plugins and discovery&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think these features are in line with the “geocoding toolbox” goal
of the project.  While there are a lot of features there, I think
geopy will still feel like a nice compact library.&lt;/p&gt;

&lt;p&gt;Why does geopy deserve some developer attention?  Because it’s being
used in numerous interesting ways, including: directing robots at Carnegie Mellon
University, calculating stream lengths for the U.S. Geological
Survey, and updating address data for the Barack Obama presidential campaign.&lt;/p&gt;

&lt;p&gt;We’ll be sprinting on Sunday, November 16th.  If anyone would like
to join us in person or on IRC, please get in touch!&lt;/p&gt;</description><link>http://blog.brianbeck.com/post/57991307</link><guid>http://blog.brianbeck.com/post/57991307</guid><pubDate>Tue, 04 Nov 2008 17:39:00 -0500</pubDate><category>geopy</category><category>c3</category><category>python</category></item><item><title>Unstoppable Rocket pumpkin.</title><description>&lt;img src="http://11.media.tumblr.com/MzaUlGORafpodu7eS1qVJIU5o1_r1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://www.unstoppablerocket.com"&gt;Unstoppable Rocket&lt;/a&gt; pumpkin.</description><link>http://blog.brianbeck.com/post/57198645</link><guid>http://blog.brianbeck.com/post/57198645</guid><pubDate>Thu, 30 Oct 2008 21:12:00 -0400</pubDate><category>unstoppable rocket</category><category>halloween</category></item><item><title>Henry the degu. ♥</title><description>&lt;img src="http://7.media.tumblr.com/MzaUlGORafmt7tgq8L6fk7G4o1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;Henry the &lt;a href="http://en.wikipedia.org/wiki/Degu"&gt;degu&lt;/a&gt;. ♥</description><link>http://blog.brianbeck.com/post/56843710</link><guid>http://blog.brianbeck.com/post/56843710</guid><pubDate>Tue, 28 Oct 2008 21:04:24 -0400</pubDate></item><item><title>Simple scheduled message queue (with threads)</title><description>&lt;p&gt;Here’s a more flexible version of the message queue in &lt;a href="http://blog.brianbeck.com/post/55070874/message-queue"&gt;my last post&lt;/a&gt;.  This version uses the &lt;a href="http://docs.python.org/library/threading.html"&gt;threading&lt;/a&gt; module instead of &lt;a href="http://pypi.python.org/pypi/processing"&gt;processing&lt;/a&gt;, so it has no dependencies.  See the new example after the code.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;"""
Simple message queue.

Messages are scheduled and processed in a single worker thread spawned
from the main process.  Thus, events are enqueued asynchronously, but
processed in a linear fashion.

"""
import time
import sched
from Queue import Queue, Empty
from threading import Thread


def delay_put(duration, queue, message):
    time.sleep(duration)
    queue.put(message)

def run_scheduler(scheduler):
    scheduler.run()

class Scheduler(sched.scheduler):
    def __init__(self, queue, handler, timeout):
        self.message_queue = queue
        self.handler = handler
        self.timeout = timeout
        sched.scheduler.__init__(self, time.time, self.delay)

    def delay(self, duration):
        queue = self.message_queue
        if duration &gt; 0:
            # Spawn a process that will sleep, enqueue None, and exit.
            Thread(target=delay_put, args=(duration, queue, None)).start()
        try:
            message = queue.get(True, duration + self.timeout) # Block!
        except Empty:
            self.timed_out()
        else:
            if message is not None:
               # A message was enqueued during the delay.
                timestamp = message.get('timestamp', self.timefunc())
                priority = message.get('priority', 1)
                self.enterabs(timestamp, priority, self.handler, (message,))

    def timed_out(self):
        print "Timed out."

    def startup(self):
        print "Starting scheduler!"

    def shutdown(self):
        print "Scheduler done."

    def run(self):
        # Schedule the `startup` event to trigger `delayfunc`.
        self.enter(0, 0, self.startup, ())
        sched.scheduler.run(self)
        self.shutdown()

class MessageQueue(object):
    def __init__(self, handler, timeout=10, scheduler_class=Scheduler):
        self.queue = Queue()
        self.scheduler = scheduler_class(self.queue, handler, timeout)
        self.worker = None

    def enqueue(self, message):
        self.queue.put(message)
        if not self.working():
            self.start_worker()

    def start_worker(self):
        self.worker = Thread(target=run_scheduler, args=(self.scheduler,))
        self.worker.start()

    def working(self):
        return self.worker is not None and self.worker.isAlive()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;input type="hidden" name="language" value="python"&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&gt;&gt;&gt; import time
&gt;&gt;&gt; def my_handler(message):
...     print time.time(), message

&gt;&gt;&gt; mq = MessageQueue(my_handler)
&gt;&gt;&gt; for i in range(1, 10):
...     now = time.time()
...     mq.enqueue({'data': i, 'timestamp': now + i})

Starting scheduler!
1224341361.32 {'timestamp': 1224341361.2808199, 'data': 1}
1224341362.3 {'timestamp': 1224341362.2912149, 'data': 2}
1224341363.31 {'timestamp': 1224341363.2913051, 'data': 3}
1224341364.32 {'timestamp': 1224341364.2913489, 'data': 4}
1224341365.32 {'timestamp': 1224341365.291404, 'data': 5}
1224341366.3 {'timestamp': 1224341366.291467, 'data': 6}
1224341367.32 {'timestamp': 1224341367.291549, 'data': 7}
1224341368.34 {'timestamp': 1224341368.291626, 'data': 8}
1224341369.34 {'timestamp': 1224341369.2921841, 'data': 9}
Timed out.
Scheduler done.
&lt;/code&gt;&lt;/pre&gt;</description><link>http://blog.brianbeck.com/post/55165808</link><guid>http://blog.brianbeck.com/post/55165808</guid><pubDate>Sat, 18 Oct 2008 10:53:06 -0400</pubDate><category>python</category><category>threading</category></item><item><title>Simple scheduled message queue in Python</title><description>&lt;p&gt;Here’s a very simple message queue using Python’s &lt;a href="http://docs.python.org/library/sched.html"&gt;sched&lt;/a&gt; module and &lt;a href="http://pyprocessing.berlios.de/doc/"&gt;processing&lt;/a&gt; (available as &lt;a href="http://docs.python.org/library/multiprocessing.html"&gt;multiprocessing&lt;/a&gt; in Python 2.6).  This lets you asynchronously schedule events to occur at a specific time.  It would be very easy to modify this to process messages with a pool of workers, or use &lt;code&gt;threading&lt;/code&gt; instead of &lt;code&gt;processing&lt;/code&gt;.  There is one thing I could use lazyweb’s help with: find places in the code where I need to use a lock or where I am ignoring &lt;a href="http://docs.python.org/library/multiprocessing.html#programming-guidelines"&gt;these guidelines&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update: &lt;a href="http://blog.brianbeck.com/post/55165808/message-queue-2"&gt;Here’s a cleaned up version using threads&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;"""
Simple message queue.

Messages are scheduled and processed in a single worker process spawned
from the main process.  Thus, events are enqueued asynchronously, but
processed in a linear fashion.

"""
import sched
import time
from processing import Queue, Process
from processing.queue import Empty


def delay_put(duration, queue, message):
    time.sleep(duration)
    queue.put(message)
    queue.close()

class Scheduler(sched.scheduler):
    def __init__(self, queue, handler):
        delayfunc = self.make_delay_func(queue, handler)
        sched.scheduler.__init__(self, time.time, delayfunc)

    def make_delay_func(self, queue, handler):
        def delay(duration):
            if duration &gt; 0:
                # Spawn a process that will sleep, enqueue None, and exit.
                Process(target=delay_put, args=(duration, queue, None)).start()
            try:
                message = queue.get(True, duration + TIMEOUT) # Block!
            except Empty:
                print "Timed out."
            else:
                if message is not None:
                    # A message was enqueued during the delay.
                    timestamp = message.get('timestamp', time.time())
                    priority = message.get('priority', 1)
                    self.enterabs(timestamp, priority, handler, (message,))
        return delay

    def startup(self):
        print "Starting scheduler!"

    def run(self):
        # Schedule the `startup` event to trigger `delayfunc`.
        self.enter(0, 0, self.startup, ())
        sched.scheduler.run(self)

def handle(message):
    print "[%s] MESSAGE: %s" % (time.time(), message)

def run_scheduler(scheduler):
    scheduler.run()
    print "Scheduler done."

QUEUE = Queue() # Message queue.  Use `enqueue` to add messages.
TIMEOUT = 10 # Seconds for scheduler to wait for items in queue.
SCHEDULER = Scheduler(QUEUE, handle) # Message handler scheduler.
PROCESS = None # Process running the scheduler.

def enqueue(message):
    global PROCESS
    QUEUE.put(message)
    if PROCESS is None or PROCESS.getExitCode() is not None:
        # There is no scheduler process running; start one.
        PROCESS = Process(target=run_scheduler, args=(SCHEDULER,))
        PROCESS.start()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here’s a usage example:
&lt;input type="hidden" name="language" value="python"&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&gt;&gt;&gt; import time
&gt;&gt;&gt; enqueue({'data': 1})
Starting scheduler!
[2008-10-17 14:33:56.212] MESSAGE: {'data': 1}

&gt;&gt;&gt; enqueue({'data': 3, 'timestamp': time.time() + 10})
&gt;&gt;&gt; enqueue({'data': 2, 'timestamp': time.time() + 7})
&gt;&gt;&gt; enqueue({'data': 4, 'timestamp': time.time() + 15})
&gt;&gt;&gt; time.sleep(26)
[2008-10-17 14:34:03.221] MESSAGE: {'timestamp': 1224268443.219, 'data': 2}
[2008-10-17 14:34:06.217] MESSAGE: {'timestamp': 1224268446.215, 'data': 3}
[2008-10-17 14:34:11.225] MESSAGE: {'timestamp': 1224268451.222, 'data': 4}
Timed out.
Scheduler done.

&gt;&gt;&gt; enqueue({'data': 5, 'timestamp': time.time() + 5})
Starting scheduler!
[2008-10-17 14:34:27.233] MESSAGE: {'timestamp': 1224268467.232, 'data': 5}
Timed out.
Scheduler done.
&lt;/code&gt;&lt;/pre&gt;</description><link>http://blog.brianbeck.com/post/55070874</link><guid>http://blog.brianbeck.com/post/55070874</guid><pubDate>Fri, 17 Oct 2008 17:02:00 -0400</pubDate><category>python</category><category>processing</category><category>multiprocessing</category></item><item><title>Cleveland Code Co-op</title><description>&lt;a href="http://nooss.org/wiki/Cleveland_Code_Co-op"&gt;Cleveland Code Co-op&lt;/a&gt;: This Sunday will be the first meeting of the &lt;a href="http://nooss.org/wiki/Cleveland_Code_Co-op"&gt;Cleveland Code Co-op&lt;/a&gt;.  Our goal is to contribute to a different Free Software project at each session, determined by the interests of those present.  This Sunday will be a &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; sprint.  All are welcome to attend.</description><link>http://blog.brianbeck.com/post/54717230</link><guid>http://blog.brianbeck.com/post/54717230</guid><pubDate>Wed, 15 Oct 2008 14:35:00 -0400</pubDate></item><item><title>Here’s a PDF of the slides and notes from my talk at...</title><description>&lt;img src="http://11.media.tumblr.com/MzaUlGORaesspv6qh3q3P56mo1_500.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;a href="http://media.brianbeck.com/files/Python_DSLs_I.pdf"&gt;Here’s a PDF of the slides and notes&lt;/a&gt; from my talk at Clepy: Ingredients for Building a DSL in Python.  Most of the content is in the notes, so zoom out if you don’t see them.  A few slides have accompanying code files, which I’ll get online later tonight.</description><link>http://blog.brianbeck.com/post/53538107</link><guid>http://blog.brianbeck.com/post/53538107</guid><pubDate>Tue, 07 Oct 2008 20:57:00 -0400</pubDate><category>python</category><category>presentation</category><category>pdf</category></item><item><title>Sounds like a bigger problem for you than me…</title><description>&lt;img src="http://6.media.tumblr.com/MzaUlGORaere65g3iAeV9O7Ho1_500.png"/&gt;&lt;br/&gt;&lt;br/&gt;Sounds like a bigger problem for you than me…</description><link>http://blog.brianbeck.com/post/53383955</link><guid>http://blog.brianbeck.com/post/53383955</guid><pubDate>Mon, 06 Oct 2008 21:22:21 -0400</pubDate></item><item><title>Python talk at October Clepy meeting</title><description>&lt;a href="http://groups.google.com/group/clepy/msg/e612f57a76382e9d"&gt;Python talk at October Clepy meeting&lt;/a&gt;: This Monday (October 6th) at Clepy, I’ll be giving a talk about the ingredients for creating DSLs in Python.  These ingredients include deferred expressions, the generative/builder pattern, metaclasses, and descriptors. &lt;a href="http://groups.google.com/group/clepy/msg/e612f57a76382e9d"&gt;See the meeting announcement for details&lt;/a&gt;.</description><link>http://blog.brianbeck.com/post/53205563</link><guid>http://blog.brianbeck.com/post/53205563</guid><pubDate>Sun, 05 Oct 2008 17:26:06 -0400</pubDate><category>python</category><category>clepy</category></item><item><title>Distributing media with Django apps</title><description>&lt;p&gt;Reusable Django apps have a sore spot right now: &lt;em&gt;media&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;Introduction&lt;/h3&gt;

&lt;p&gt;Django takes a hands-off approach to your media files.  Your project has two settings, &lt;code&gt;MEDIA_URL&lt;/code&gt; (the base URL where your media is located) and &lt;code&gt;MEDIA_ROOT&lt;/code&gt; (the filesystem path where media files are stored).  The only thing it does with these is put &lt;code&gt;FileField&lt;/code&gt; files under &lt;code&gt;MEDIA_ROOT&lt;/code&gt;.  The rest — collecting the required files, serving them up somehow, and ensuring that they’re loaded from &lt;code&gt;MEDIA_URL&lt;/code&gt; in templates — is completely up to the developer.  This mostly works out great, since many sites will have a dedicated media server, and developers probably know better than Django which media files should be loaded where.  It isn’t perfect, however, because there is one thing Django could help with: collecting the required files.&lt;/p&gt;

&lt;h3&gt;The problem&lt;/h3&gt;

&lt;p&gt;Some Django apps — &lt;a href="http://code.google.com/p/django-batchadmin/"&gt;batchadmin&lt;/a&gt;, for example — distribute media (CSS, images, JavaScript) that is necessary (or recommended) for the app to work.  Doing this is currently ad hoc and annoying &lt;em&gt;for both the app developer and the app user&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Since there is only one &lt;code&gt;MEDIA_ROOT&lt;/code&gt;, the app’s files have to get in there somehow.  How do they get there?  Who knows, but the person installing the app has to do it manually.  Okay, now the app should be able to access its media, right?  Maybe.  It depends, where did the user copy/link/move those files to, anyway?  The app must now define its own settings so the user can tell it where they put the app’s media files in &lt;code&gt;MEDIA_ROOT&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;The solution&lt;/h3&gt;

&lt;p&gt;The approach I propose is hopefully &lt;strong&gt;the simplest thing that could possibly work&lt;/strong&gt;.  I think that approach is to just find all media files in the project’s &lt;code&gt;INSTALLED_APPS&lt;/code&gt; and put them in &lt;code&gt;MEDIA_ROOT&lt;/code&gt;.  This idea has resulted in a &lt;a href="http://www.djangosnippets.org/snippets/1068/"&gt;collectmedia&lt;/a&gt; management command that can be run once by the user at installation or deployment time.  This way, if you have apps laid out like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;app1/media/
app1/media/app1/
app1/media/app1/css/style.css
app1/media/app1/js/forms.js
app2/media/
app2/media/app2/
app2/media/app2/css/fonts.css
app2/media/app2/img/icon.png
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;…then running &lt;code&gt;collectmedia&lt;/code&gt; will let the apps reference their media like so (in a template, for example):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{{ MEDIA_URL }}app1/css/style.css
{{ MEDIA_URL }}app2/css/fonts.css
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Just like with reusable app templates, it should be best practice to make a subdirectory under &lt;code&gt;media&lt;/code&gt; with the same name as the app.  This way, app2 can override app1’s style by including a file with the path &lt;code&gt;app2/media/app1/css/style.css&lt;/code&gt;.  And just like with templates, when multiple apps provide a file with the same relative path, the app listed in &lt;code&gt;INSTALLED_APPS&lt;/code&gt; first is selected.&lt;/p&gt;

&lt;p&gt;This approach avoids having Django try to do any sort of dynamic dispatching of media files, because that would eliminate the advantage of using a media server completely independent of Django.&lt;/p&gt;

&lt;h3&gt;Speak up&lt;/h3&gt;

&lt;p&gt;If you have ideas or opinions on this matter, and especially if you want to see something like this included in Django, check out the &lt;a href="https://groups.google.com/group/django-developers/browse_thread/thread/94efe43b1d1c7787"&gt;relevant thread on the django-developers mailing list&lt;/a&gt;.&lt;/p&gt;</description><link>http://blog.brianbeck.com/post/50940622</link><guid>http://blog.brianbeck.com/post/50940622</guid><pubDate>Fri, 19 Sep 2008 23:48:00 -0400</pubDate></item><item><title>django-batchadmin: Batch actions in the admin change list</title><description>&lt;p&gt;I’m releasing a new project tonight: &lt;a href="http://code.google.com/p/django-batchadmin/"&gt;django-batchadmin&lt;/a&gt;.  It looks like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://media.brianbeck.com/images/batchadmin-1.png" alt="Django with batchadmin enabled"/&gt;&lt;/p&gt;

&lt;p&gt;Plenty of people have done this before with &lt;a href="http://www.djangosnippets.org/snippets/626/"&gt;recipes&lt;/a&gt; or &lt;a href="http://franxman.com/browsable/django-special-ops/"&gt;patches&lt;/a&gt;.  But this is a project, and it’s distributed as a Django app, not with copy &amp; paste or a diff.  Also, since it’s being released post-1.0, it uses the latest and greatest in &lt;code&gt;newforms-admin&lt;/code&gt; features.&lt;/p&gt;

&lt;p&gt;The app is very minimal but customizable.  There’s only one action included in the app: delete.  Refer to the &lt;a href="http://code.google.com/p/django-batchadmin/wiki/GettingStarted"&gt;Getting Started&lt;/a&gt; page to add your own actions.&lt;/p&gt;

&lt;p&gt;All functionality is added with a &lt;code&gt;ModelAdmin&lt;/code&gt; subclass called &lt;code&gt;BatchModelAdmin&lt;/code&gt;.  This simply puts a checkbox in each row, specifies a change list template that wraps a form around the list, and makes the view accept POST requests, which it dispatches to the action code.&lt;/p&gt;

&lt;p&gt;Stay tuned for some custom actions.&lt;/p&gt;</description><link>http://blog.brianbeck.com/post/50177198</link><guid>http://blog.brianbeck.com/post/50177198</guid><pubDate>Sun, 14 Sep 2008 22:02:00 -0400</pubDate><category>django</category><category>python</category></item><item><title>Texts Rasterization Exposures</title><description>&lt;a href="http://antigrain.com/research/font_rasterization/index.html"&gt;Texts Rasterization Exposures&lt;/a&gt;: As the guy who notices when a single pixel is off, I love this article about font rasterization.  I’ve had it bookmarked for at least a year and sometimes read it just for fun.</description><link>http://blog.brianbeck.com/post/42387376</link><guid>http://blog.brianbeck.com/post/42387376</guid><pubDate>Tue, 15 Jul 2008 18:58:43 -0400</pubDate></item><item><title>The New Yorker: I have to pay a lot of taxes living in New York City. What about you, are you a taxes guy?&#13;</title><description>The New Yorker: I have to pay a lot of taxes living in New York City. What about you, are you a taxes guy?&lt;br /&gt;&#13;
Chris Onstad: I pay almost all the major taxes, and I’m also part of a local program in my town where we pay experimental taxes to see if we get mad.&lt;br /&gt;&#13;
</description><link>http://blog.brianbeck.com/post/41779945</link><guid>http://blog.brianbeck.com/post/41779945</guid><pubDate>Thu, 10 Jul 2008 13:24:31 -0400</pubDate></item></channel></rss>
