<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-25736244</id><updated>2012-01-23T09:09:39.511-08:00</updated><category term='lepton'/><category term='movie'/><category term='perlin noise'/><category term='postgres'/><category term='pyglet'/><category term='python'/><category term='ideas'/><category term='shader'/><category term='database'/><category term='psycopg'/><title type='text'>:: eat the dots ::</title><subtitle type='html'>game over, man</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>35</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-25736244.post-3385896836610510788</id><published>2011-10-15T22:26:00.000-07:00</published><updated>2011-10-15T22:26:36.707-07:00</updated><title type='text'>The Power to do it Wrong</title><content type='html'>I believe I am not alone in finding myself, more often then I might like, having to make less than ideal decisions in the world of software development. I find myself lamenting inconsistencies and warts, in tools, runtimes, and applications themselves. It is a universe of our own making, sometimes my own personal making, and often it seems to tend toward a proverbial hell rather than heaven.&lt;br /&gt;&lt;br /&gt;The process of realizing something, manifesting it from the ideal abstract notion to concrete reality, is a process of compromise. The ideal world cares not of constraints, contradictions, complications, differing opinions, and arbitrary decisions of the past. The ideal world does not care that the human mind and body have physical limitations, and that we cannot sustain long between breaths, meals, and checking twitter. We may assume so, but the ideal world is not lenient, it knows no deviance from ideal elegance. It tolerates nothing that is not perfect, in fact it flatly refuses the existence of imperfection.&lt;br /&gt;&lt;br /&gt;There are software tools, and systems, and quite popular ones at that, that seem bent on imposing an ideal model, a "one true way", if you will. I am tolerant of many things, I am not a man of overbearing opinion. I like to think that everyone is largely always wrong, myself included, and I find some pleasure in life watching that prove to be true. But, there is one&amp;nbsp;philosophy&amp;nbsp;that I simply cannot abide as a result, the one true way. This is not something I learned, as Lady Gaga would say, I was just born this way. But my personal inability to deal with dogma is not the point here.&lt;br /&gt;&lt;br /&gt;Perfection is assumed to be a great and wonderful thing, how could it be not? What about all those quaint stories of falling from grace, the eternal struggle to regain that which we lost through our own greed and arrogance? The great temptation, the human condition,&amp;nbsp;transcending&amp;nbsp;evil, and all that? Wasn't software supposed to be "easy?" Some sort of mathematical paradise devoid of human blight? Yeah well, only the kind of software to which we deridingly refer, you know the kind with "no users."&lt;br /&gt;&lt;br /&gt;Ok, so I'm saying perfection is bad? No, I say perfection is tyrannical, it is the ultimate one true way. However, I needn't let that throw me into an existential funk. Because, if there is one truth that I can hold dear, it is that perfection is impossible. And even if it were possible, I could not exist there, thus any resulting self-loathing is self-prevented.&lt;br /&gt;&lt;br /&gt;But, crucially, the notion of perfection, of the ideal, of the simple, of the absence of compromise is not bad. In fact it is an essential human quality. It's a strange notion that chasing something impossible, something dubious, is noble, but that's what we do. Every developer that I know that's worth their salt wants to find the elusive ideal, or at minimum the least worst solution for the problem at hand. The mind of the master knows that perfection of the whole is impossible, yet will still strive to simplify, to idealize, to remove compromise where it can. Mastery itself is largely the ability to direct that effort most effectively, and to know when it is futile.&lt;br /&gt;&lt;br /&gt;So, we must embrace that the universe, the software, the hardware, and the wetware is imperfect, and deal with it. And those tools, and&amp;nbsp;technologies&amp;nbsp;that best deal with that reality are the ones we should embrace. Often, it is not even a matter of embracing, but more of natural selection. Those tools that over-idealize, that preach the one true way, will eventually lose. Yes, worse is better.&lt;br /&gt;&lt;br /&gt;So the next time you are working on a web page, and you find that no amount of dogmatic&amp;nbsp;adherence&amp;nbsp;to CSS best practices will fix your layout, be glad that the tools are bigger, dare I say better, than the ideal. Sure, you may say a few choice words when you have to use a &amp;lt;br/&amp;gt; or *gasp* a &amp;lt;table&amp;gt; tag. But think about what you would say if there were no workaround. If the tools assumed perfection, we'd all be screwed.&lt;br /&gt;&lt;br /&gt;Great technologies are imperfect, they are flawed, they break their own rules. Great people are no different, though often their faces are more symmetrical than yours, but I digress. The point is that there will never be perfect software tools, and you should be wary of those purporting the one true way. Besides, perfection is boring, and imposed perfection is stifling. We need tools with abstractions that leak, with models that don't always map, and with escape valves that let us do it wrong. Because sometimes doing it wrong is the best way, the only way it can be done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-3385896836610510788?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/3385896836610510788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=3385896836610510788' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/3385896836610510788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/3385896836610510788'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2011/10/power-to-do-it-wrong.html' title='The Power to do it Wrong'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-734940059184048714</id><published>2010-08-17T21:31:00.000-07:00</published><updated>2010-08-17T21:35:56.140-07:00</updated><title type='text'>Running Tests for Multiple Pythons</title><content type='html'>For my &lt;a href="http://bitbucket.org/caseman/planar/"&gt;Planar&lt;/a&gt; project, I'm committed to being compatible for Python 2.6-3.1 and beyond. Since this project contains C extensions, that means that I need to be able to build and test for each Python conveniently. And yes, different Pythons do behave differently, have different compilation gripes, and sometimes even expose different bugs due to internal differences in GC, etc. &lt;br /&gt;&lt;br /&gt;This shell script I wrote is a simple, but effective solution. It requires that all of the versions of Python you are testing are installed (of course), and also have nose installed. It is very easy to extend to more Python versions as well by modifying the &lt;tt&gt;for&lt;/tt&gt; loop at the top. You run it from the top-level source directory, alongside your &lt;tt&gt;setup.py&lt;/tt&gt;. I imagine other folks have come up with more elegant solutions than this, but I figured it might be useful to someone, so here it is:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;#!/bin/bash&lt;br /&gt;# Build from scratch and run unit tests &lt;br /&gt;# from python 2.6, 2.7 and 3.1&lt;br /&gt;# Then run doctests to verify doc examples&lt;br /&gt;# Note: requires nose installed in each python instance&lt;br /&gt;&lt;br /&gt;error=0&lt;br /&gt;&lt;br /&gt;rm -rf build&lt;br /&gt;&lt;br /&gt;for ver in 2.6 2.7 3.1; do&lt;br /&gt; echo "************"&lt;br /&gt; echo " Python $ver"&lt;br /&gt; echo "************"&lt;br /&gt; echo&lt;br /&gt; if which python${ver}; then&lt;br /&gt;  python${ver} setup.py build &amp;&amp; \&lt;br /&gt;  python${ver} -m nose.core \&lt;br /&gt;   -d -w build/lib.*${ver}/ --with-coverage $@ || error=1&lt;br /&gt; else&lt;br /&gt;  echo &gt;&amp;2 "!!! Python ${ver} not found !!!"&lt;br /&gt;  error=1&lt;br /&gt; fi&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;echo&lt;br /&gt;echo -n "Doctests... "&lt;br /&gt;srcdir=`pwd`&lt;br /&gt;cd build/lib.*3.?/ &amp;&amp; \&lt;br /&gt;  python3 -m doctest ${srcdir}/doc/source/*.rst &amp;&amp; \&lt;br /&gt;  echo "OK" || error=1&lt;br /&gt;&lt;br /&gt;exit $error&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The bottom part runs doctests you might have in &lt;tt&gt;doc/source&lt;/tt&gt;, which is where I keep the sphinx doc sources.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-734940059184048714?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/734940059184048714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=734940059184048714' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/734940059184048714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/734940059184048714'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2010/08/running-tests-for-multiple-pythons.html' title='Running Tests for Multiple Pythons'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-3852180134371717810</id><published>2010-04-26T21:26:00.000-07:00</published><updated>2010-04-26T21:26:45.672-07:00</updated><title type='text'>Planar 0.1</title><content type='html'>&lt;a href="http://pygamesf.org/~casey/planar/doc/"&gt;&lt;img src="http://pygamesf.org/~casey/planar/doc/_static/planar.png"/&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Planar is a 2D geometry library I'm working on for use by my &lt;a href="http://pygamesf.org/~casey/grease/doc/"&gt;Grease game engine&lt;/a&gt;. For maximum usefulness, however, I'm distributing planar as a separate library.&lt;br /&gt;&lt;br /&gt;Since it doesn't have any external dependancies of its own that dictate otherwise, I took the liberty of making it compatible with both Python 2.6+ and 3.1+. Planar implements everything in both Python and C, so it was a good opportunity to finally jump into 3.x with both feet. I was pleased with how easily &lt;tt&gt;2to3&lt;/tt&gt; worked to seamlessly take care of the necessary Python code mungification. I was also pleased with the C side of the world as well. Conceptually things haven't changed too much in 3.x, and a few &lt;tt&gt;#ifdefs&lt;/tt&gt; here and there and some macros took care of the API differences there.&lt;br /&gt;&lt;br /&gt;Once I got it initially working in Python 3, I found myself coding against it and fixing 2.x incompatibilities afterwards. I think that's a good sign. &lt;br /&gt;&lt;br /&gt;The functionality of 0.1 is very basic, but it's fully documented and tested, so I'm satisfied that I'm ready to move on and implement more. Now that I've learned a bunch of the Python 3 ropes, I'm hopeful that I can make fast progress toward 0.2. &lt;br /&gt;&lt;br /&gt;Head over the the &lt;a href="http://pygamesf.org/~casey/planar/doc/"&gt;planar docs site&lt;/a&gt; to learn more. Feedback is encouraged and appreciated.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-3852180134371717810?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/3852180134371717810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=3852180134371717810' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/3852180134371717810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/3852180134371717810'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2010/04/planar-01.html' title='Planar 0.1'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-6455915876805176148</id><published>2010-04-14T23:38:00.000-07:00</published><updated>2010-04-14T23:46:49.026-07:00</updated><title type='text'>Arg Parsing Micro-Benchmark</title><content type='html'>I'm working on a vector class for my shiny new&amp;nbsp;&lt;a href="http://bitbucket.org/caseman/planar/"&gt;Planar&lt;/a&gt;&amp;nbsp;geometry Python&amp;nbsp;library. When I was working on Lepton (which is mostly in C), I was keenly aware of the overhead of the generic arg parsing C-APIs (&lt;tt&gt;PyArg_ParseTupleAndKeywords&lt;/tt&gt; and friends). Since this vector class largely consists of methods that do very little work, but may be called fairly often, I figured this overhead should be considered.&lt;br /&gt;&lt;br /&gt;I implemented a class method to instantiate a vector with polar coordinates, which are converted to cartesian. Here it is in Python:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;class Vec2(tuple):&lt;br /&gt;&lt;br /&gt;    def __new__(self, x, y):&lt;br /&gt;        return tuple.__new__(Vec2, &lt;br /&gt;            ((x * 1.0, y * 1.0)))&lt;br /&gt;&lt;br /&gt;    @classmethod&lt;br /&gt;    def polar(cls, angle, length=1.0):&lt;br /&gt;        """Create a vector from polar coordinates"""&lt;br /&gt;        radians = math.radians(angle)&lt;br /&gt;        vec = tuple.__new__(cls, &lt;br /&gt;            (math.cos(radians) * length, &lt;br /&gt;             math.sin(radians) * length))&lt;br /&gt;        return vec&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Note, using &lt;tt&gt;tuple.__new__&lt;/tt&gt; in &lt;tt&gt;polar&lt;/tt&gt; is an optimization that saves a layer of method calls and skips converting the x and y values to floats, since I know they are already. Running this through timeit with Python 3.1.2, I get:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&gt;&gt;&gt; timeit.timeit('Vec2.polar(20, 10)', &lt;br /&gt;... 'from planar.vector import Vec2')&lt;br /&gt;2.3068268299102783&lt;br /&gt;&gt;&gt;&gt; timeit.timeit('Vec2.polar(angle=20, length=10)', &lt;br /&gt;... 'from planar.vector import Vec2')&lt;br /&gt;2.3426671028137207&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Notice there's not much difference in timing between positional and keyword arguments.&lt;br /&gt;&lt;br /&gt;Now let's implement the &lt;tt&gt;polar&lt;/tt&gt; class method in C using generic arg parsing. Here's the method's code:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;static PyObject *&lt;br /&gt;Vec2_polar(PyTypeObject *type, PyObject *args, PyObject *kwargs)&lt;br /&gt;{&lt;br /&gt;    PlanarVec2Object *v;&lt;br /&gt;    double angle;&lt;br /&gt;    double length = 1.0;&lt;br /&gt;&lt;br /&gt;    static char *kwlist[] = {"angle", "length", NULL};&lt;br /&gt;&lt;br /&gt;    assert(PyType_IsSubtype(type, &amp;PlanarVec2Type));&lt;br /&gt;    if (!PyArg_ParseTupleAndKeywords(&lt;br /&gt;        args, kwargs, "f|f:Vec2.polar()", &lt;br /&gt;        kwlist, &amp;angle, &amp;length)) {&lt;br /&gt;        return NULL;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    v = (PlanarVec2Object *)type-&gt;tp_alloc(type, 0);&lt;br /&gt;    if (v != NULL) {&lt;br /&gt;        angle = radians(angle);&lt;br /&gt;        v-&gt;x = cos(angle) * length;&lt;br /&gt;        v-&gt;y = sin(angle) * length;&lt;br /&gt;    }&lt;br /&gt;    return (PyObject *)v;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Here's the timing for the above:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&gt;&gt;&gt; timeit.timeit('Vec2.polar(20, 10)', &lt;br /&gt;... 'from planar.cvector import Vec2')&lt;br /&gt;1.045346975326538&lt;br /&gt;&gt;&gt;&gt; timeit.timeit('Vec2.polar(angle=20, length=10)', &lt;br /&gt;... 'from planar.cvector import Vec2')&lt;br /&gt;1.578913927078247&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;This is certainly faster than the Python code, but not tremendously so, though that's not too surprising given the simplicity of this method. However, what is surprising is the performance difference between positional and keyword arguments. But, there it is.&lt;br /&gt;&lt;br /&gt;Since this method takes two arguments, and they are both floats, maybe we can speed up the positional case, which should be common, by doing the arg parsing ourselves. We know that &lt;tt&gt;PyObject *args&lt;/tt&gt; is a tuple, so let's take it apart manually and extract the angle and length. A slight complication is that the second &lt;tt&gt;length&lt;/tt&gt; argument is optional, but we can handle it. Here's the beautiful result:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;static PyObject *&lt;br /&gt;Vec2_new_polar(PyTypeObject *type, PyObject *args, PyObject *kwargs)&lt;br /&gt;{&lt;br /&gt;    PyObject *angle_arg;&lt;br /&gt;    PyObject *length_arg;&lt;br /&gt;    PlanarVec2Object *v;&lt;br /&gt;    int arg_count;&lt;br /&gt;    double angle;&lt;br /&gt;    double length = 1.0;&lt;br /&gt;&lt;br /&gt;    static char *kwlist[] = {"angle", "length", NULL};&lt;br /&gt;&lt;br /&gt;    assert(PyType_IsSubtype(type, &amp;PlanarVec2Type));&lt;br /&gt;    if (kwargs == NULL) {&lt;br /&gt;        /* No kwargs, do fast manual arg handling */&lt;br /&gt;        arg_count = PyTuple_GET_SIZE(args);&lt;br /&gt;        if (arg_count != 1 &amp;&amp; arg_count != 2) {&lt;br /&gt;            PyErr_SetString(PyExc_TypeError, &lt;br /&gt;                "Vec2.polar(): wrong number of arguments");&lt;br /&gt;            return NULL;&lt;br /&gt;        }&lt;br /&gt;        angle_arg = PyTuple_GET_ITEM(args, 0);&lt;br /&gt;        if (!PyNumber_Check(angle_arg)) {&lt;br /&gt;            PyErr_SetString(PyExc_TypeError, &lt;br /&gt;                "Vec2.polar(): expected number for argument angle");&lt;br /&gt;            return NULL;&lt;br /&gt;        }&lt;br /&gt;        angle_arg = PyNumber_Float(angle_arg);&lt;br /&gt;        if (angle_arg == NULL) {&lt;br /&gt;            return NULL;&lt;br /&gt;        }&lt;br /&gt;        angle = PyFloat_AS_DOUBLE(angle_arg);&lt;br /&gt;        Py_CLEAR(angle_arg);&lt;br /&gt;        if (arg_count == 2) {&lt;br /&gt;            length_arg = PyTuple_GET_ITEM(args, 1);&lt;br /&gt;            if (!PyNumber_Check(length_arg)) {&lt;br /&gt;                PyErr_SetString(PyExc_TypeError, &lt;br /&gt;                    "Vec2.polar(): expected number for argument length");&lt;br /&gt;                return NULL;&lt;br /&gt;            }&lt;br /&gt;            length_arg = PyNumber_Float(length_arg);&lt;br /&gt;            if (length_arg == NULL) {&lt;br /&gt;                Py_DECREF(angle_arg);&lt;br /&gt;                return NULL;&lt;br /&gt;            }&lt;br /&gt;            length = PyFloat_AS_DOUBLE(length_arg);&lt;br /&gt;            Py_CLEAR(length_arg);&lt;br /&gt;        }&lt;br /&gt;    } else if (!PyArg_ParseTupleAndKeywords(&lt;br /&gt;        args, kwargs, "f|f:Vec2.polar()", &lt;br /&gt;        kwlist, &amp;angle, &amp;length)) {&lt;br /&gt;        return NULL;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    v = (PlanarVec2Object *)type-&gt;tp_alloc(type, 0);&lt;br /&gt;    if (v != NULL) {&lt;br /&gt;        angle = radians(angle);&lt;br /&gt;        v-&gt;x = cos(angle) * length;&lt;br /&gt;        v-&gt;y = sin(angle) * length;&lt;br /&gt;    }&lt;br /&gt;    return (PyObject *)v;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;So, if &lt;tt&gt;kwargs&lt;/tt&gt; is &lt;tt&gt;NULL&lt;/tt&gt;, then we extract the angle, and the length if present from the &lt;tt&gt;args&lt;/tt&gt; tuple. We check if each are numbers, convert them to float objects (so you can pass in ints without complaint), then we extract the C &lt;tt&gt;double&lt;/tt&gt; from each to store in the vector object's struct. Sure looks like a lot of code, let's see if it buys us anything:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&gt;&gt;&gt; timeit.timeit('Vec2.polar(20, 10)', &lt;br /&gt;... 'from planar.cvector import Vec2')&lt;br /&gt;0.46842408180236816 &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;em&gt;Bam!&lt;/em&gt; Not bad at all! Of course if you do pass keyword args, we fall back to generic parsing, so that should perform the same as the last one:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&gt;&gt;&gt; timeit.timeit('Vec2.polar(angle=20, length=10)', &lt;br /&gt;... 'from planar.cvector import Vec2')&lt;br /&gt;1.5973320007324219&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;If I were feeling ambitious, I could extract the args from the &lt;tt&gt;PyObject *kwargs&lt;/tt&gt; dict manually as well, and see some speedup. But at some point the added code and maintenance cost isn't worth it. I know the opportunity is there if needed, but also that passing args positionally is already a lot faster (a fact that is likely worth documenting).&lt;br /&gt;&lt;br /&gt;I'm writing this planar library such that every class is coded in Python and C. Also the library is compatible with both Python 2.6+ and Python 3.x. If you're interested in an example of how to write a package that's compatible with Python 2 and 3 with C-extensions, &lt;a href="http://bitbucket.org/caseman/planar/src/"&gt;check it out&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-6455915876805176148?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/6455915876805176148/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=6455915876805176148' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6455915876805176148'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6455915876805176148'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2010/04/arg-parsing-micro-benchmark.html' title='Arg Parsing Micro-Benchmark'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-8483153938534669540</id><published>2010-03-22T19:13:00.000-07:00</published><updated>2010-03-22T19:13:08.966-07:00</updated><title type='text'>Frameworks, Libraries, Namespaces and Distributions</title><content type='html'>I've been busy not working on the code for my game engine framework Grease recently. I've actually been working on the documentation, but since I can't help myself, I've been thinking a good bit about the code, and specifically what I want to work on next.&lt;br /&gt;&lt;br /&gt;The functionality that I plan to work on next is rather general 2D geometry and vector graphics. The important thing is that although Grease will depend on these things, they are not part of the framework per se. They should be useful for folks who don't buy into Grease's framework dogma, or don't need its features. Simply put they will be libraries that could be perfectly happy outside of Grease or any framework.&lt;br /&gt;&lt;br /&gt;So, humble denizens of the Python planet, what is the best approach to naming and packaging these libraries for distribution? I see two possible options, both with pros and cons:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Put them under the &lt;tt&gt;grease&lt;/tt&gt; "brand" packaging them as something like: &lt;tt&gt;grease.geometry&lt;/tt&gt; and &lt;tt&gt;grease.vg&lt;/tt&gt;. They would be distributed separately and probably together with grease the framework as well.&lt;/li&gt;&lt;li&gt;Give them their own independent name (I'm leaning toward &lt;tt&gt;flatly&lt;/tt&gt;) and package them separately from grease, though grease would depend on them.&lt;/li&gt;&lt;/ol&gt;#1 has some advantages:&lt;ol&gt;&lt;/ol&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Less "brand" complexity. It's obvious that &lt;tt&gt;grease.vg&lt;/tt&gt; is part of the grease project, and when used inside of grease there's one less arbitrary brand-name to remember.&lt;/li&gt;&lt;li&gt;It's easy to envision these libraries fitting snugly with the rest of the framework in terms of documentation and what not.&lt;/li&gt;&lt;li&gt;It's pretty easy to justify folding the release cycle of the libraries in that of the framework.&lt;/li&gt;&lt;/ul&gt;And some disadvantages:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;The independence of these subpackages is unclear, folks may be reluctant to use them if they feel that they carry too much framework baggage or cognitive complexity.&lt;/li&gt;&lt;li&gt;I'm not sure the best way to actually make these separate from a distutils&amp;nbsp;perspective, or whatever distribution flavor of the minute is in fashion. I suspect they should just live in their own directory trees and get stitched into the &lt;tt&gt;grease&lt;/tt&gt; top-level package at installation-time. I'm wary of drawbacks and unintended side-affects of that, however.&lt;/li&gt;&lt;li&gt;They're nested rather than flat (sorry Tim).&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;#2 also has some advantages:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;The independence of the library package(s) is obvious.&lt;/li&gt;&lt;li&gt;&amp;nbsp;It's rather obvious how to organize them from a distribution perspective.&lt;/li&gt;&lt;li&gt;They aren't weighed down by any framework connotations.&lt;/li&gt;&lt;li&gt;The namespace is flatter.&lt;/li&gt;&lt;/ul&gt;And of course disadvantages:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;They don't promote the framework "brand" in any way and may be perceived as less well integrated with the framework.&lt;/li&gt;&lt;li&gt;They would feel like more independent creatures to me, thus I think I would tend to fuss about them individually more. That's a subjective thing, of course, and not necessarily bad for users.&lt;/li&gt;&lt;li&gt;It's not as clear to me how to deal with them as grease dependencies. Bundle them or fetch them at installation time?&lt;/li&gt;&lt;/ul&gt;Honestly I'm able to justify both ways to myself, and of course it's not a matter of life-and-death. But it does feel like an important api decision and I value any opinion or ideas you may have on the matter. So please comment if you feel inclined.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Also I'm interested in people's opinions on bundling vs. fetching dependencies at installation. In the bad old days, bundling was basically a no-brainer with Python, but given things like setuptools/distribute and buildout, the latter has gained appeal. Which do you prefer?&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-8483153938534669540?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/8483153938534669540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=8483153938534669540' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/8483153938534669540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/8483153938534669540'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2010/03/frameworks-libraries-namespaces-and.html' title='Frameworks, Libraries, Namespaces and Distributions'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-6683388266752894160</id><published>2010-02-28T12:24:00.000-08:00</published><updated>2010-02-28T12:35:02.148-08:00</updated><title type='text'>Grease 0.1 Released</title><content type='html'>I released the inaugural 0.1 release of my game engine Grease today. This is basically a &lt;i&gt;throw-it-over-the-wall&lt;/i&gt; release so that I can use the code and push it further during the upcoming &lt;a href="http://www.pyweek.org/"&gt;pyweek&lt;/a&gt; compo at the end of March. That said it is complete enough to implement an entire game, an example of which is included in the distribution. Obligatory screenshots below:&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_cLcxaL2sjC4/S4rFQ4vMfqI/AAAAAAAAAk0/wjACTA1hyBo/s1600-h/blasteroids1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="314" src="http://4.bp.blogspot.com/_cLcxaL2sjC4/S4rFQ4vMfqI/AAAAAAAAAk0/wjACTA1hyBo/s400/blasteroids1.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_cLcxaL2sjC4/S4rFbyGERVI/AAAAAAAAAk8/atC9BBJ7JqE/s1600-h/blasteroids2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="312" src="http://4.bp.blogspot.com/_cLcxaL2sjC4/S4rFbyGERVI/AAAAAAAAAk8/atC9BBJ7JqE/s400/blasteroids2.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;The game uses Grease features such as its awesomely-retro vector polygon renderer, collision detection, decorator-based key binding and mode management for hot-seat multiplayer. The entire game is a single script that weighs in about 550 lines, which includes whitespace and docstrings. Even so no real effort is made to make the code short, it was written with clarity in mind, not brevity. Most of the code savings comes from the abstractions available in Grease.&lt;br /&gt;&lt;br /&gt;At the moment, Grease is implemented entirely in Python and sits on top of Pyglet. My upcoming efforts on this project will be to make the blasteroids example into a full tutorial and get everything fully documented. After that I will be creating more complex example games and adding native-code parts to Grease where needed. The intention is to always have pure-Python versions of everything available though, and the Python versions will be developed first.&lt;br /&gt;&lt;br /&gt;Of course you are welcome and encouraged to try it for yourself. You can download it from pypi here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pypi.python.org/pypi/grease"&gt;http://pypi.python.org/pypi/grease&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The code of the example blasteroids game above can be scrutinized here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/caseman/source/browse/trunk/grease/examples/blasteroids.py"&gt;blasteroids.py&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-6683388266752894160?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/6683388266752894160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=6683388266752894160' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6683388266752894160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6683388266752894160'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2010/02/grease-01-released.html' title='Grease 0.1 Released'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_cLcxaL2sjC4/S4rFQ4vMfqI/AAAAAAAAAk0/wjACTA1hyBo/s72-c/blasteroids1.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-5352163159069626575</id><published>2010-01-16T23:00:00.000-08:00</published><updated>2010-02-28T12:38:52.195-08:00</updated><title type='text'>Decorate Thy Keyboard Controls</title><content type='html'>As part of a game engine called &lt;i&gt;Grease&lt;/i&gt; I'm working on (more on that sometime soon) I was thinking about ways to create a clean and efficient api for handling keyboard events. Too often this ubiquitous section of game code consists of a tangle of if/elif statements, a construct that is a personal pet peeve of mine. So to avoid that mess, a typical strategy I've employed is to map keys to methods using a dictionary for easy dispatch. Doing this for one-off applications is simple enough, if not particularly clean and tidy.&lt;br /&gt;&lt;br /&gt;Anyway taking a step back for a sec, let's examine some goals. Basically what I'm after is a way to define some methods (or even functions) that get executed in response to key events. Specifically there are three types of key events that I'm interested in: key press, key release and key hold. The first two get dispatched once per key "stroke" as you'd expect. The key hold event fires every game "tick"  that a key remains down; useful for continuous functions like thrust, etc. &lt;br /&gt;&lt;br /&gt;So I need a clean way to map methods to specific keys and key event types. Sounds like a job for: decorators! Truth be told I haven't felt the need to write many decorators, but after prototyping a non-decorator version that came out less than clean even though it only supported one type of key event, I thought I'd give decorators a go. Below is an example of how to implement key controls using what I've cooked up:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;class PlayerControls(KeyControls):&lt;br /&gt;&lt;br /&gt;    @KeyControls.key_press(key.LEFT)&lt;br /&gt;    def start_turn_left(self):&lt;br /&gt;        ship.rotation = -ship.turn&lt;br /&gt;    &lt;br /&gt;    @KeyControls.key_release(key.LEFT)&lt;br /&gt;    def stop_turn_left(self):&lt;br /&gt;        if ship.rotation &amp;lt; 0:&lt;br /&gt;            ship.rotation = 0&lt;br /&gt;&lt;br /&gt;    @KeyControls.key_press(key.RIGHT)&lt;br /&gt;    def start_turn_right(self):&lt;br /&gt;        ship.rotation = ship.turn&lt;br /&gt;    &lt;br /&gt;    @KeyControls.key_release(key.RIGHT)&lt;br /&gt;    def stop_turn_right(self):&lt;br /&gt;        if ship.rotation &amp;gt; 0:&lt;br /&gt;            ship.rotation = 0&lt;br /&gt;&lt;br /&gt;    @KeyControls.key_hold(key.UP)&lt;br /&gt;    def thrust(self, dt):&lt;br /&gt;        ship.body.apply_local_force(&lt;br /&gt;            ship.player.thrust * dt)&lt;br /&gt;    &lt;br /&gt;    @KeyControls.key_press(key.P)&lt;br /&gt;    def pause(self, dt):&lt;br /&gt;        global paused&lt;br /&gt;        paused = not paused&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;Here's the code needed to wire this into pyglet:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;window = pyglet.window.Window()&lt;br /&gt;controls = KeyControls(window)&lt;br /&gt;pyglet.clock.schedule_interval(controls.run, 1.0/60.0)&lt;br /&gt;pyglet.app.run()&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Though using the decorators binds the keys to specific methods of the class at compile-time, KeyControls also contains additional methods for changing the key bindings at run-time. I may also add support to load and store key bindings from a configuration file if that feature is needed.&lt;br /&gt;&lt;br /&gt;This implementation is designed to work with pyglet, though I think the same general approach could be used with pygame. The module for this, although part of a larger project I am working on, doesn't depend on anything else. Feel free to give it a try and see what you think.&lt;br /&gt;&lt;br /&gt;[&lt;b&gt;Edit&lt;/b&gt; a new version of this module is now available]&lt;br /&gt;&lt;br /&gt;&lt;a href="http://caseman.googlecode.com/svn/trunk/grease/grease/pygletsys.py"&gt;Get the module here.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-5352163159069626575?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/5352163159069626575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=5352163159069626575' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/5352163159069626575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/5352163159069626575'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2010/01/decorate-thy-keyboard-controls.html' title='Decorate Thy Keyboard Controls'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-6258700931238712065</id><published>2010-01-11T16:12:00.000-08:00</published><updated>2010-01-11T16:12:52.925-08:00</updated><title type='text'>_______ is Better Than _______</title><content type='html'>If explicit truly was better than implicit, Python code would look like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;tt&gt;    StringBuilder s = new StringBuilder()&lt;/tt&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And let's face it, that's not enlightening to anyone.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-6258700931238712065?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/6258700931238712065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=6258700931238712065' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6258700931238712065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6258700931238712065'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2010/01/is-better-than.html' title='_______ is Better Than _______'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-2540373948339413659</id><published>2009-07-14T10:20:00.000-07:00</published><updated>2009-07-14T10:21:22.726-07:00</updated><title type='text'>Lepton featured in Python Magazine</title><content type='html'>In a nice little article by yours truly:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pymag.phparch.com/c/issue/view/100"&gt;http://pymag.phparch.com/c/issue/view/100&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-2540373948339413659?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/2540373948339413659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=2540373948339413659' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/2540373948339413659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/2540373948339413659'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2009/07/lepton-featured-in-python-magazine.html' title='Lepton featured in Python Magazine'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-1651131859423072305</id><published>2009-03-18T21:37:00.000-07:00</published><updated>2009-03-19T06:45:18.509-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ideas'/><category scheme='http://www.blogger.com/atom/ns#' term='lepton'/><title type='text'>Lepton Particle Targeting</title><content type='html'>The other feature I alluded to in my last post was targeting, which allows you to specify specific starting and ending values for particle attributes and interpolate between them over time. This is used often in spontaneous arrangement effects, such as particles "imploding" from random directions into an orderly pattern, such as an image or logo. But many other effects are possible, since it gives you close control over arbitrary attributes and how  they change over time without having to manage all of that complexity directly.&lt;br /&gt;&lt;br /&gt;Here is how I envision particle targeting to work in Lepton:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Create a particle group to contain the "target" particles. Populate this group with particles with the desired final state for your interpolated particles.&lt;br /&gt;&lt;li&gt;Create a particle group to contain the interpolated particles with a target controller pointing at the target particle group.&lt;br /&gt;&lt;li&gt;Create or emit particles into the interpolated group however you see fit. Each new particle is matched with a particle in the target group. This matching can be done automatically or manually.&lt;br /&gt;&lt;li&gt;Each time the interpolated group is updated, particle attributes are interpolated between the initial particle state and the target particle state using a tweening function.&lt;/ul&gt;The magic happens in the target controller itself. When you instantiate it, you specify the target particle group, the attributes to interpolate, each attribute's tweening function and the particle age where it reaches its target. Since each interpolated particle can have a different initial age value, this means particles can reach their final state in different times. Typically the target particles would not be drawn or even change, but there would be no restriction on that and that could be used to create even more interesting effects.&lt;br /&gt;&lt;br /&gt;Here's some theoretical sample code for targeting:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;target_particles = ParticleGroup()&lt;br /&gt;# ... populate target_particles ...&lt;br /&gt;terps = ParticleGroup(&lt;br /&gt;    controllers=[&lt;br /&gt;        TargetRandom(target_particles, &lt;br /&gt;            position=EaseInOutQuad(10), &lt;br /&gt;            up=EaseInCirc(8)),&lt;br /&gt;    ])&lt;/pre&gt;&lt;/code&gt;There will be several types of target controllers: &lt;tt&gt;Target&lt;/tt&gt; will require interpolated particles to be matched to their targets manually, &lt;tt&gt;TargetRandom&lt;/tt&gt; will randomly select targets, and &lt;tt&gt;TargetSequential&lt;/tt&gt; will select targets in the order they occur in the target group. The interpolation behavior will be the same for these controller classes.&lt;br /&gt;&lt;br /&gt;Interpolation of each attribute will be controlled by tweening functions. The keyword arguments passed to the target controller specify which particle attributes to interpolate and which tweening function to use. The simplest tween is just a linear interpolator, but more complex tweens, such as easings will create more natural looking movement and morphing. You can find a great discussion of tweening and easings &lt;a href="http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf"&gt;in this document&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-1651131859423072305?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/1651131859423072305/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=1651131859423072305' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1651131859423072305'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1651131859423072305'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2009/03/lepton-particle-targetting.html' title='Lepton Particle Targeting'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-2603450532482105189</id><published>2009-03-17T21:57:00.000-07:00</published><updated>2009-03-17T22:14:55.083-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ideas'/><category scheme='http://www.blogger.com/atom/ns#' term='lepton'/><title type='text'>Better texture support in Lepton</title><content type='html'>Now that &lt;a href="http://code.google.com/p/py-lepton/"&gt;Lepton 0.9a&lt;/a&gt; is out the door, it's time to think about the next steps in the particle engine's evolution. In particular, there are two main features I'd like to work on for the next release:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Greatly improved texture support&lt;br /&gt;&lt;li&gt;Interpolating particle attributes from a specific starting state to an ending state.&lt;/ul&gt;&lt;br /&gt;The former will be addressed with "texturizers". These will have the following functionality:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Handle the requisite loading of textures into the graphics card&lt;br /&gt;&lt;li&gt;Perform necessary texture-related state changes before rendering particles in a group&lt;br /&gt;&lt;li&gt;Computing texture coordinates for each active particle in the group&lt;br /&gt;&lt;li&gt;Restore the texture state to what is was prior to rendering the group&lt;/ul&gt;&lt;br /&gt;Here's a tentative interface for a texturizer:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;class Texturizer:&lt;br /&gt;&lt;br /&gt; def set_state(self):&lt;br /&gt;   """This method sets the appropriate &lt;br /&gt;   OpenGL state for rendering. It is called &lt;br /&gt;   immediately before rendering a group of particles.&lt;br /&gt;   """&lt;br /&gt; &lt;br /&gt; def restore_state(self):&lt;br /&gt;   """Restores the OpenGL state to what it was &lt;br /&gt;   before the call to set_state().&lt;br /&gt;   It is called after rendering a group of particles.&lt;br /&gt;   """&lt;br /&gt;&lt;br /&gt; @property&lt;br /&gt; def tex_dimension(self):&lt;br /&gt;   """The number of texture coordinates per vertex, &lt;br /&gt;   either 2 or 3&lt;br /&gt;   """&lt;br /&gt;&lt;br /&gt; def generate_tex_coords(self, group, coord_array):&lt;br /&gt;   """Generate texture coordinates for all active &lt;br /&gt;   particles in the group, placing them in the &lt;br /&gt;   cooresponding places in coord_array, an&lt;br /&gt;   array of floats. &lt;br /&gt;  &lt;br /&gt;   Each particle gets four texture positions &lt;br /&gt;   cooresponding to its lower left, lower right, upper &lt;br /&gt;   right and upper left corners respectively.&lt;br /&gt;   The number of coordinates per texture position is &lt;br /&gt;   determined by the value of tex_dimension.&lt;br /&gt;   """&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;The texturizer will be instantiated by the user for use with a renderer. The arguments needed to instantiate the texturizer will vary depending on the type of texturizer. Most will accept one or more texture objects (probably borrow some pyglet code for those) and other ancillary arguments to control specific features. Here's an example of how you might use a simple texturizer:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;my_texture = Texture('amazing_image.png')&lt;br /&gt;particles = ParticleGroup(&lt;br /&gt;   controllers=[...controllers go here..],&lt;br /&gt;   renderer=BillboardRenderer(SpriteTexturizer(my_texture)))&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;This would simply apply the same texture derived from the image file &lt;tt&gt;amazing_image.png&lt;/tt&gt; to each particle in the group. By default it would perform bilinear filtering to scale the texture and would clamp at the edges (i.e., it would not tile).&lt;br /&gt;&lt;br /&gt;Here are some ideas for different texturizers:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;SpriteTexturizer&lt;/tt&gt; &lt;blockquote&gt;Could apply one or more texture images to a group of particles. The images would be packed into a single texture atlas and the texturizer would select them by varying the texture coordinates per particle.  The images could be applied sequentially (i.e., round-robin) to each particle in a group, or randomly. It may also be possible to apply weights to each sprite image to have some applied more often than others.&lt;/blockquote&gt;&lt;br /&gt;&lt;tt&gt;AnimatedTexturizer&lt;/tt&gt; &lt;blockquote&gt;Could apply a sequence of images to all particles in a group, like above the image frames would be packed into a single texture object in OpenGL. The animation frame assigned to each particle would be based on that particle's age. It would have an option for packing the frames into a 3D texture, so you could smoothly interpolate between the frames on the hardware. This would allow smooth animation with fewer frames.&lt;/blockquote&gt;&lt;br /&gt;&lt;tt&gt;MultiTexturizer&lt;/tt&gt; &lt;blockquote&gt;Could apply multiple textures to all particles in a group simultaneously. This would allow various effects by blending multiple textures together. One usage would be to have a large base texture that may also be tiled. The base texture could stay stationary relative to the particles. A second "splat" texture would be applied to each particle to show the base texture at that particle's position. As particles moved they would reveal different parts of the base texture. The splat texture could have sharp or fuzzy edges depending on the desired effect. This could be used for various simple animation techniques, or different stylized rendering.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-2603450532482105189?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/2603450532482105189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=2603450532482105189' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/2603450532482105189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/2603450532482105189'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2009/03/better-texture-support-in-lepton.html' title='Better texture support in Lepton'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-5738530697271559425</id><published>2009-01-23T15:51:00.000-08:00</published><updated>2009-01-23T16:02:14.403-08:00</updated><title type='text'>On bogus threat levels...</title><content type='html'>&lt;p&gt;I fly every few weeks now and for the last who knows how long the threat level has been orange which supposedly represents a "high" threat of a terrorist attack.&lt;br /&gt;&lt;p&gt;to which Steve Mirsky in his regular column in Scientific American has this great retort:&lt;br /&gt;&lt;blockquote&gt;Here’s how you know that the terrorist threat isn’t really high: the airport is still open, and your flight hasn’t been canceled.&lt;/blockquote&gt;&lt;br /&gt;&lt;a href="http://www.sciam.com/article.cfm?id=not-a-close-shave"&gt;Read the whole column here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-5738530697271559425?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/5738530697271559425/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=5738530697271559425' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/5738530697271559425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/5738530697271559425'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2009/01/on-bogus-threat-levels.html' title='On bogus threat levels...'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-2963912702356669679</id><published>2009-01-08T16:12:00.000-08:00</published><updated>2009-01-08T16:30:30.517-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pyglet'/><title type='text'>Multi-texture support for pyglet vertex lists</title><content type='html'>I wrote this little hack to allow easy expression of multiple sets of texture coordinates in pyglet vertex lists. I wrote it so I could use the &lt;a href="http://en.wikipedia.org/wiki/Texture_splatting"&gt;texture splatting&lt;/a&gt; technique to draw a terrain map for a game. For that purpose it works very well.  It does a small bit of monkey-patching to pyglet itself, so it may break if things are refactored, but it works good for 1.1.x.&lt;br /&gt;&lt;br /&gt;To use it, you instantiate your vertex list the normal way except you can specify multiple sets of texture coordinates. Then you call &lt;tt&gt;multitexture.enable()&lt;/tt&gt; passing it your new vertex list. Here's a simple example:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;    vl = pyglet.graphics.vertex_list(4,&lt;br /&gt;        ('v2f', (100, 100, 200, 100, 200, 200, 100, 200)),&lt;br /&gt;        ('t2f', (0, 0, 1, 0, 1, 1, 0, 1)),&lt;br /&gt;        ('t2f', (0, 0, 0.25, 0, 0.25, 0.25, 0, 0.25)),&lt;br /&gt;        )&lt;br /&gt;    multitexture.enable(vl)&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;You can &lt;a href="http://caseman.googlecode.com/svn/trunk/mech/mechazoid/multitexture.py"&gt;get the module here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-2963912702356669679?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/2963912702356669679/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=2963912702356669679' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/2963912702356669679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/2963912702356669679'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2009/01/multi-texture-support-for-pyglet-vertex.html' title='Multi-texture support for pyglet vertex lists'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-1540064366415120371</id><published>2008-08-28T16:31:00.000-07:00</published><updated>2008-08-28T18:59:11.204-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='psycopg'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='postgres'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Faking read() support for psycopg's copy_from()</title><content type='html'>This has been a burr in my side for a long time. If you've ever tried to use &lt;tt&gt;copy_from()&lt;/tt&gt; in psycopg2 from a source other than a real file, you've probably run into this issue: &lt;tt&gt;copy_from()&lt;/tt&gt; requires a file-like object that supports both &lt;tt&gt;readline()&lt;/tt&gt; and &lt;tt&gt;read()&lt;/tt&gt; &lt;span style="font-weight:bold;"&gt;(!)&lt;/span&gt; methods! This is not an issue if the source of the data is already coming from a file or pipe, but what if you are synthesizing the data line-by-line? Like, I don't know, parsing a huge honking log file and trying to insert it en masse into Postgres? Then what? &lt;br /&gt;&lt;br /&gt;Well, I've implemented a semi-ridiculous workaround class that implements &lt;tt&gt;read()&lt;/tt&gt; by buffering up a bunch of lines until it's got enough to shovel off to psycopg2, but of course, lines never exactly fill the requested number of bytes, so you've got to deal with that and you're left with a pretty damn messy, and stupid &lt;tt&gt;read()&lt;/tt&gt; method. Ugh. &lt;br /&gt;&lt;br /&gt;Reading the psycopg2 source (ain't open source grand? ;^) made me realize a couple of things. One: &lt;tt&gt;readline()&lt;/tt&gt; is only called with older protocols (i.e., older postgres versions) and would never be used in my case. So really only &lt;tt&gt;read()&lt;/tt&gt; was even used at all. Ok, that's interesting, but &lt;tt&gt;read()&lt;/tt&gt; is the problem-child here, so that doesn't soothe the irritation.&lt;br /&gt;&lt;br /&gt;The second thing I saw from the code was that psycopg doesn't care how many bytes &lt;tt&gt;read()&lt;/tt&gt; returns at all. As long as it returns &lt;span style="font-weight:bold;"&gt;some bytes&lt;/span&gt; it's happy. So that made me realize that &lt;tt&gt;read()&lt;/tt&gt; could just return the same thing as &lt;tt&gt;readline()&lt;/tt&gt; and it should still work. I fired up vim and punched in some test code to verify:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;import psycopg2&lt;br /&gt;&lt;br /&gt;class ReadFaker:&lt;br /&gt;&lt;br /&gt;    def __init__(self, data):&lt;br /&gt;        self.iter = iter(data)&lt;br /&gt;&lt;br /&gt;    def readline(self, size=None):&lt;br /&gt;        try:&lt;br /&gt;            line = self.iter.next()&lt;br /&gt;        except StopIteration:&lt;br /&gt;            return ''&lt;br /&gt;        else:&lt;br /&gt;            return "%s\t%s\n" % line&lt;br /&gt;&lt;br /&gt;    read = readline&lt;br /&gt;&lt;br /&gt;fakefile = ReadFaker([&lt;br /&gt;    ('foo', 'bar'),&lt;br /&gt;    ('baz', 'spam'),&lt;br /&gt;    ('bar', 'foo'),&lt;br /&gt;    ('foo', 'foo'),&lt;br /&gt;    ])&lt;br /&gt;&lt;br /&gt;db = psycopg2.connect(database='test')&lt;br /&gt;c = db.cursor()&lt;br /&gt;c.copy_from(fakefile, 'foo')&lt;br /&gt;db.commit()&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;Then I created a &lt;tt&gt;test&lt;/tt&gt; table with two text columns in a local database named test. Voila! It works!&lt;br /&gt;&lt;br /&gt;Of course to be proper, my &lt;tt&gt;readline()&lt;/tt&gt; method shouldn't ignore its input &lt;tt&gt;size&lt;/tt&gt; argument, but that's probably more trouble then its worth practically.&lt;br /&gt;&lt;br /&gt;Someday I think this use-case should be handled better by psycopg2, i.e., you should just be able to pass it an iterable object of equal-length sequences for each record. Then it can compose the textual representation to throw off to Postgres. There are complexities there around complex types like dates and times, but even if it only handled textual and numeric types and made you deal with the others it would be an improvement. Maybe a "spare time" project for me some day 8^)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-1540064366415120371?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/1540064366415120371/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=1540064366415120371' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1540064366415120371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1540064366415120371'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/08/faking-read-support-for-psycopgs.html' title='Faking read() support for psycopg&apos;s copy_from()'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-8221032102480422533</id><published>2008-08-19T15:05:00.000-07:00</published><updated>2008-08-19T21:17:42.246-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shader'/><category scheme='http://www.blogger.com/atom/ns#' term='pyglet'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='perlin noise'/><category scheme='http://www.blogger.com/atom/ns#' term='movie'/><title type='text'>Cloudy with a Chance of...</title><content type='html'>I got inspired to add another example to my &lt;a href="http://pypi.python.org/pypi/noise/"&gt;Python noise library&lt;/a&gt;. This one creates a real-time animated atmosphere around planet Earth. It uses the GLSL fast fake noise implementation in the library.&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="350"&gt; &lt;param name="movie" value="http://www.youtube.com/v/phIvIVxruzU"&gt; &lt;/param&gt; &lt;embed src="http://www.youtube.com/v/phIvIVxruzU" type="application/x-shockwave-flash" width="425" height="350"&gt; &lt;/embed&gt; &lt;/object&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://caseman.googlecode.com/files/atmosphere.avi"&gt;(download a higher quality video)&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;The example uses &lt;a href="http://www.pyglet.org"&gt;pyglet&lt;/a&gt;. I cooked up a nice little class for capturing frames to png files that can be encoded into a video. It was used to make the above:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;class MovieCapture:&lt;br /&gt;&lt;br /&gt;    def __init__(self, dirname):&lt;br /&gt;        assert os.path.isdir(dirname), (&lt;br /&gt;            "%s is not a valid directory" % dirname)&lt;br /&gt;        self.dirname = dirname&lt;br /&gt;        self.frame_no = 0&lt;br /&gt;&lt;br /&gt;    def capture(self):&lt;br /&gt;        """Capture the current window a file, &lt;br /&gt;        return the file name"""&lt;br /&gt;        filename = 'frame-%05d.png'&lt;br /&gt;        while os.path.exists(os.path.join(&lt;br /&gt;            self.dirname, filename % self.frame_no)):&lt;br /&gt;            self.frame_no += 1&lt;br /&gt;        mgr = pyglet.image.get_buffer_manager()&lt;br /&gt;        mgr.get_color_buffer().save(&lt;br /&gt;            os.path.join(self.dirname, &lt;br /&gt;            filename % self.frame_no))&lt;br /&gt;        return filename % self.frame_no&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;When you instantiate it, you provide a path to a directory to write the captured frames into. Each is written to a sequentially named png file. After drawing each frame, you simply call the &lt;tt&gt;capture()&lt;/tt&gt;  method to write it to a new file. Once you've got the frames you need, you can encode them into a movie using mencoder or whatever tool you choose. To make this video I used mencoder like so:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;mencoder "mf://atmosphere/*.png" -mf fps=20 -o atmosphere.avi -ovc lavc -lavcopts vcodec=msmpeg4v2:vbitrate=800&lt;/tt&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-8221032102480422533?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/8221032102480422533/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=8221032102480422533' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/8221032102480422533'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/8221032102480422533'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/08/cloudy-with-chance-of.html' title='Cloudy with a Chance of...'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-6732800693649091625</id><published>2008-07-25T13:45:00.000-07:00</published><updated>2008-07-25T13:46:51.921-07:00</updated><title type='text'>Things that will not change in Python 3000</title><content type='html'>A useful PEP for you to read:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.python.org/dev/peps/pep-3099/"&gt;http://www.python.org/dev/peps/pep-3099/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-6732800693649091625?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/6732800693649091625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=6732800693649091625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6732800693649091625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/6732800693649091625'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/07/things-that-will-not-change-in-python.html' title='Things that will not change in Python 3000'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-1190556945587038686</id><published>2008-07-17T10:16:00.000-07:00</published><updated>2008-07-17T10:24:56.097-07:00</updated><title type='text'>Why is C faster than Python?</title><content type='html'>Questioning the obvious is something we do a lot accidently (and get chided for it), but occasionally it yields some useful insights. A while back, there was a discussion on the pygame mailing list about languages and speed. The poster was insistent that there was no good reason why C was faster than Python. Below is my explanation, which in writing I realized I had never voiced this way before:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;When there is no better algorithm, then using a higher performing language is about the only option you have for better performance. There is an even better option, dedicated hardware, but that is far beyond the reach of most mere mortals. But that's the reason why we have things like SIMD instruction sets, GPUs and physics co-processors.&lt;br /&gt;&lt;br /&gt;All this conjecturing about Python performance in relation to compiled languages makes me wonder. Why is the performance of C code not as good as that of well-written hand-coded assembler? Surely if the machine can do it, C can?&lt;br /&gt;&lt;br /&gt;The problem boils down to one of specificity. Any language that tells the computer more specifically what to do can be faster than one that doesn't. Unfortunately, telling the computer what to do specifically is not a productive way to solve most problems. I'd rather not have to tell the computer which machine codes to execute, but if I did, and I was clever, I could make my program the fastest possible -- for a given specific architecture at least.&lt;br /&gt;&lt;br /&gt;Python is more abstract and general than C, C is more abstract and general than machine code. Therefore machine code is potentially faster than C, and C is potentially faster than Python. And unless you reduce the generality of Python, it will always be possible to write faster programs in C, given a competent compiler.&lt;br /&gt;&lt;br /&gt;JITs (e.g., psyco) can help mitigating this, but they too are ultimately constrained by Python's expressive power.&lt;br /&gt;&lt;br /&gt;So unless you are proposing to reduce Python's expressive power, you are waging a losing battle. Until which time machine intelligence exceeds our own, it will always be possible for a human programmer to get higher performance from the lower-level language. And I further propose that when the time arrives that machine intelligence exceeds our own, this will not be the problem foremost on my mind ;^)&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-1190556945587038686?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/1190556945587038686/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=1190556945587038686' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1190556945587038686'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1190556945587038686'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/07/why-is-c-faster-than-python.html' title='Why is C faster than Python?'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-4379961475534873066</id><published>2008-05-26T00:08:00.000-07:00</published><updated>2008-12-10T06:21:27.759-08:00</updated><title type='text'>Shader Noise</title><content type='html'>After some gnashing of teeth -- mostly involving "stupid" stuff like creating a proper 2-channel 3D texture and computing lighting vectors -- I have working shader noise functions in the noise library. The raw noise functions do 3D Perlin, fbm, and turbulent noise. To demo them I created a script that bump-maps three rotating spheres with some simple real-time procedural textures. The first is just a bumpy texture using straight Perlin noise, the second is a crinkly texture using 4 octaves of fbm noise. The third (bottom) is an embossed turbulent noise texture.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cLcxaL2sjC4/SDpqq9F8msI/AAAAAAAAAA0/Gthy7NyH-a0/s1600-h/bumpy.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_cLcxaL2sjC4/SDpqq9F8msI/AAAAAAAAAA0/Gthy7NyH-a0/s400/bumpy.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5204589605566585538" /&gt;&lt;/a&gt;&lt;br /&gt;Note that all three spheres use exactly the same vertex geometry, the apparent differences in the shapes are entirely due to the shaders. The effect is even better with a moving image, as is usual with these types of things.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/caseman/"&gt;Get the code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-4379961475534873066?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/4379961475534873066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=4379961475534873066' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/4379961475534873066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/4379961475534873066'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/05/shader-noise.html' title='Shader Noise'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_cLcxaL2sjC4/SDpqq9F8msI/AAAAAAAAAA0/Gthy7NyH-a0/s72-c/bumpy.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-4644033901339318885</id><published>2008-05-20T12:58:00.000-07:00</published><updated>2008-12-10T06:21:27.930-08:00</updated><title type='text'>Giant Planet Noise</title><content type='html'>Among other things, I've been recently working on a &lt;a href="http://www.noisemachine.com/talk1/index.html"&gt;Perlin noise&lt;/a&gt; library for use in Python games. It does all the heavy lifting in native code, for speed. I'm also working on a shader-based noise function so you can do texture generation on the GPU. I have a few examples of how to use the noise functions, and the other day I added one to generate textures for planets (gas giants to start with). Below are three screen shots illustrating how the texture is composed. The first shot is just several octaves of noise stretched parallel to the equator of the planet mixing two colors. The next adds turbulence by warping the first texture using input from a second noise call. The third uses the turbulence value to alter the luminance of the colors, simulating up and down-drafts in the atmosphere. Given the simplicity of the code and short dev time, I was quite happy with the results.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_cLcxaL2sjC4/SDRQJtDtkXI/AAAAAAAAAAs/i2QTTC8fGH8/s1600-h/planets.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_cLcxaL2sjC4/SDRQJtDtkXI/AAAAAAAAAAs/i2QTTC8fGH8/s400/planets.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5202871597163975026" /&gt;&lt;/a&gt;&lt;br /&gt;If I were to use these in a game, I would add some additional features: For one I would use per-pixel lighting instead of the default per-vertex in the fixed functionality to get rid of some ugly faceting artifacts on the shadow border and specular highlight. Also, some sort of planetary glow shading would be nice. I'll probably add the latter to the example sometime.&lt;br /&gt;&lt;br /&gt;I haven't released the noise library yet, but it does work and you can grab it from my &lt;a href="http://code.google.com/p/caseman/source/checkout"&gt;Google code repository&lt;/a&gt; if you want to play with it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-4644033901339318885?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/4644033901339318885/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=4644033901339318885' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/4644033901339318885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/4644033901339318885'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/05/giant-planet-noise.html' title='Giant Planet Noise'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_cLcxaL2sjC4/SDRQJtDtkXI/AAAAAAAAAAs/i2QTTC8fGH8/s72-c/planets.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-8000091580763753040</id><published>2008-04-02T23:10:00.000-07:00</published><updated>2008-04-02T23:35:06.044-07:00</updated><title type='text'>Technique Treasure Trove</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://graphics.pixar.com/Geri/thumbNail.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px;" src="http://graphics.pixar.com/Geri/thumbNail.png" border="0" alt="" /&gt;&lt;/a&gt;I've recently been on a quest to find the best technique for real-time noise generation in OpenGL. Being able to generate textures is not only good for realism, but a crucial labor-saving device for indie developers with limited resources. While digging around, I came upon a very interesting &lt;a href="http://graphics.pixar.com/WaveletNoise/paper.pdf"&gt;technical memo on wavelet noise&lt;/a&gt;, which greatly reduces certain aliasing artifacts that can occur when Perlin noise and it's many variants are scaled. As cool as this info is, what's even better is the fact that it's part of the larger &lt;a href="http://graphics.pixar.com/"&gt;Pixar online library&lt;/a&gt;. I have a distinct feeling that this will be consuming many hours of my time...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-8000091580763753040?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/8000091580763753040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=8000091580763753040' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/8000091580763753040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/8000091580763753040'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/04/technique-treasure-trove.html' title='Technique Treasure Trove'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-3331050646640845176</id><published>2008-03-09T11:43:00.000-07:00</published><updated>2008-04-02T23:41:41.487-07:00</updated><title type='text'>SimCity 4's Graphics Engine</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://simcity.ea.com/images/about/inside_scoop/3d6_big.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px;" src="http://simcity.ea.com/images/about/inside_scoop/3d6_big.jpg" border="0" alt="" /&gt;&lt;/a&gt;I found &lt;a href="http://simcity.ea.com/about/inside_scoop/3d1.php"&gt;this discussion&lt;/a&gt; of SimCity 4's graphics engine very interesting. Apparently SC4 is the first version to go with a truly 3D polygonal geometry, though for a variety of reasons discussed in the article they limit the view angles for highest quality and speed. I also found their analysis on the last page that current graphics technology is far better suited to first-person games than god-games like SimCity pretty interesting. Makes sense especially since an FP perspective naturally limits the amount of geometry that must be rendered in high detail.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-3331050646640845176?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/3331050646640845176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=3331050646640845176' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/3331050646640845176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/3331050646640845176'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/03/simcity-4s-graphics-engine.html' title='SimCity 4&apos;s Graphics Engine'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-5238505506074877198</id><published>2008-03-08T00:05:00.000-08:00</published><updated>2008-12-10T06:21:28.232-08:00</updated><title type='text'>Taking Flight</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_cLcxaL2sjC4/R9JKFmOMnSI/AAAAAAAAAAU/di5p8spoqNI/s1600-h/taking-flight.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_cLcxaL2sjC4/R9JKFmOMnSI/AAAAAAAAAAU/di5p8spoqNI/s400/taking-flight.jpg" alt="" id="BLOGGER_PHOTO_ID_5175280381822016802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Here is a screenshot from my latest pet-project, a helicopter flight sim. It's written using Python (of course) and &lt;a href="http://www.pyglet.org/"&gt;Pyglet &lt;/a&gt;. This is my first foray into 3D graphics, and I'm having a lot of fun learning OpenGL and training my brain to think in 4x4 transformation matrices ;^)&lt;br /&gt;&lt;br /&gt;Writing a semi-realistic simulation (I don't think I can do better having never flown a real helicopter) is a bit humbling and gives me a a lot respect for aerospace engineers who have to make real heavier-than-air objects fly in a controllable fashion. The latter part is the crucial detail and I have spent many an hour trying to figure out why my poor virtual bird (note real pilots don't call them "choppers") would starting flipping out of control seemingly of its own volition.&lt;br /&gt;&lt;br /&gt;The terrain is randomly generated by a little python script I wrote last weekend. If you want to play with that part, you can get it from a &lt;a href="http://groups.google.com/group/pyglet-users/web/fractal-terrain-generator"&gt;page I made for it&lt;/a&gt; in the Pyglet google group. It's kinda fun and can generate a variety of different terrain types.&lt;br /&gt;&lt;br /&gt;Both the terrain script and the prototype heli.py script shown above are available via svn from &lt;a href="http://code.google.com/p/caseman/source/checkout"&gt;my code repository&lt;/a&gt;. Note the word prototype above. The script is very basic, slightly better than a proof-of-concept and not well factored yet. That said I find it fun to fly around. You'll need Python, Pyglet (1.1) and &lt;a href="http://pyode.sourceforge.net/"&gt;PyODE&lt;/a&gt; to run it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-5238505506074877198?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/5238505506074877198/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=5238505506074877198' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/5238505506074877198'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/5238505506074877198'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2008/03/taking-flight.html' title='Taking Flight'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_cLcxaL2sjC4/R9JKFmOMnSI/AAAAAAAAAAU/di5p8spoqNI/s72-c/taking-flight.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-9065036647284479454</id><published>2007-06-19T13:13:00.000-07:00</published><updated>2007-06-19T13:31:04.173-07:00</updated><title type='text'>SQLAlchemy and SqlSoup Rule!</title><content type='html'>SQLAlchemy is a python ORM mapper along the lines of Hibernate.&lt;br /&gt;&lt;br /&gt;This morning I was trying to figure out how to map all the tables into model objects so I could expose them in this system inventory app I'm working on. Also, I  wanted similar functionality to the hibernate session manager. After writing a bunch of code, I discovered that this does everything I need:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;db = SqlSoup('postgres://%s:%s/%s' % (dbhost, dbport, dbname))&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;(where the db* vars are either passed in or derived from a  config). I have a table in my database called &lt;tt&gt;pc_hardware&lt;/tt&gt;, after  executing the above, with no further configuration whatsoever in the program, I can do the following:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;newpc = db.pc_hardware.insert(mac_address='00:16:cb:8c:b5:86', model='macbook pro')&lt;br /&gt;db.flush()&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;(Confusingly flush() actually means commit)&lt;br /&gt;Then in a later session I can do this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;print db.pc_hardware.select()&lt;br /&gt;[MappedPc_hardware(hardware_id=3L, mac_address='00:16:cb8c:b5:86', cpu_info=None, cpu_cores=None, gb_ram=None, power_supplies=None, console=None, model='macbook pro', form_factor=None, purchase_date=None, receive_date=None, vendor=None, location=None, status='new', last_modified=datetime.datetime(2007, 6,  19, 12, 45, 6, 659993),notes=None)]&lt;br /&gt;&lt;br /&gt;pc = db.pc_hardware.get(3)&lt;br /&gt;pc.gb_ram = 16&lt;br /&gt;db.flush()&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I can even get at the column definitions if need be:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;pprint(list(db.pc_hardware.c))&lt;br /&gt;[Column(u'hardware_id', PGInteger(), primary_key=True, nullable=False, &lt;br /&gt;default=PassiveDefault(&lt;sqlalchemy.sql._TextClause object at 0xb78b7f8c&gt;)), &lt;br /&gt;Column(u'mac_address', PGMacAddr(), nullable=False), &lt;br /&gt;Column(u'cpu_info', PGText(length=None, convert_unicode=False)), &lt;br /&gt;Column(u'cpu_cores', PGInteger()), &lt;br /&gt;Column(u'gb_ram', PGInteger()), &lt;br /&gt;Column(u'power_supplies', PGInteger()), &lt;br /&gt;Column(u'console', PGBoolean()), &lt;br /&gt;Column(u'model', PGText(length=None, convert_unicode=False)), &lt;br /&gt;Column(u'form_factor', PGText(length=None, convert_unicode=False)), &lt;br /&gt;Column(u'purchase_date', PG2Date()), &lt;br /&gt;Column(u'receive_date', PG2Date()), &lt;br /&gt;Column(u'vendor', PGText(length=None, convert_unicode=False)), &lt;br /&gt;Column(u'location', PGText(length=None, convert_unicode=False)), &lt;br /&gt;Column(u'status', PGText(length=None, convert_unicode=False), ForeignKey &lt;br /&gt;(u'hardware_status.status'), nullable=False, default=PassiveDefault(&lt;sqlalchemy.sql._TextClause object at 0xb78bd62c&gt;)), Column(u'last_modified', PG2DateTime(timezone=False), nullable=False, default=PassiveDefault &lt;br /&gt;(&lt;sqlalchemy.sql._TextClause object at 0xb78bd6ec&gt;)), &lt;br /&gt;Column(u'notes', PGText(length=None, convert_unicode=False))]&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;this is just one of the tables in the db, it automagically maps all the others as well if I access them. SQLAlchemy also creates  &lt;br /&gt;associations based on foreign key contraints, but I haven't played with that yet. Here are some more docs on the SqlSoup extension:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.sqlalchemy.org/trac/wiki/SqlSoup"&gt;http://www.sqlalchemy.org/trac/wiki/SqlSoup&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-9065036647284479454?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/9065036647284479454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=9065036647284479454' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/9065036647284479454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/9065036647284479454'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2007/06/sqlalchemy-and-sqlsoup-rule.html' title='SQLAlchemy and SqlSoup Rule!'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-1030822658343742435</id><published>2007-04-16T16:43:00.000-07:00</published><updated>2007-04-16T16:50:58.599-07:00</updated><title type='text'>Vim tip of the moment -- Recalling visual selection</title><content type='html'>I use visual selection quite a bit in vim, it's quite handy for constraining find/replace operations and the usual select+action type of operations. One annoying thing though is that I often want to perform &lt;i&gt;multiple&lt;/i&gt; actions to a given text block I have selected, but vim deselects the block after performing an action. This is ok if you just want to repeat the same action, you can just use &lt;tt&gt;.&lt;/tt&gt; (period), but if you want to do something else, that's too bad for you.&lt;br /&gt;&lt;br /&gt;Of course there is an easy way to get the previous selection back, I just didn't know about it until now: &lt;tt&gt;gv&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Just use that to swap the current selection with the last one. Now it's easy to perform a series of actions on a given selection, yay!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-1030822658343742435?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/1030822658343742435/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=1030822658343742435' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1030822658343742435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/1030822658343742435'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2007/04/vim-tip-of-moment-recalling-visual.html' title='Vim tip of the moment -- Recalling visual selection'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-4833956486367997831</id><published>2007-02-13T16:51:00.000-08:00</published><updated>2007-02-13T17:35:17.650-08:00</updated><title type='text'>Determining who's blocking who in Postgres</title><content type='html'>If you have databases like I do with lots of concurrent queries, you can sometime run into situations where you issue a query and it just hangs there blocked. Or, more likely somebody or something issues a query and then comes calling when it doesn't seem to be doing anything.&lt;br /&gt;&lt;br /&gt;Of course, you have the handy &lt;tt&gt;pg_stat_activity&lt;/tt&gt; and &lt;tt&gt;pg_locks&lt;/tt&gt; views at your disposal, but when it comes to determining exactly which queries are blocking which others and on what table, querying those alone is a tedious way to get the answer. What you really need is a query that sums it all up, in one neat and tidy bundle. Well, my friends here is such a query:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT &lt;br /&gt;  bl.procpid as blocked_pid, &lt;br /&gt;  bl.usename as user, &lt;br /&gt;  bl.current_query as blocked_query, &lt;br /&gt;  bl.query_start, &lt;br /&gt;  relname as blocked_on , &lt;br /&gt;  lq.procpid as blocking_pid,&lt;br /&gt;  lq.usename as user, &lt;br /&gt;  lq.current_query as blocking_query, &lt;br /&gt;  lq.query_start, &lt;br /&gt;  pgl2.mode as lock_type&lt;br /&gt;FROM pg_stat_activity bl, pg_locks pgl1, &lt;br /&gt;pg_stat_activity lq, pg_locks pgl2, pg_class&lt;br /&gt;WHERE bl.procpid = pgl1.pid &lt;br /&gt;AND not pgl1.granted &lt;br /&gt;AND pg_class.oid = pgl1.relation&lt;br /&gt;AND pgl2.relation = pgl1.relation &lt;br /&gt;AND pgl2.granted &lt;br /&gt;AND lq.procpid = pgl2.pid;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;In extended mode (&lt;tt&gt;\x&lt;/tt&gt;) psql returns something along these lines for this query:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;blocked_pid    | 21418&lt;br /&gt;user           | sueuser&lt;br /&gt;blocked_query  | insert values ('foo', 'bar', 'baz') &lt;br /&gt;  into extremely_large_table;&lt;br /&gt;query_start    | 2007-02-13 15:14:06.77606-08&lt;br /&gt;blocked_on     | extremely_large_table&lt;br /&gt;blocking_pid   | 21417&lt;br /&gt;user           | joeuser&lt;br /&gt;blocking_query | delete from extremely_large_table;&lt;br /&gt;query_start    | 2007-02-13 14:45:34.637675-08&lt;br /&gt;lock_type      | AccessExclusiveLock&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-4833956486367997831?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/4833956486367997831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=4833956486367997831' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/4833956486367997831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/4833956486367997831'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2007/02/determining-whos-blocking-who-in.html' title='Determining who&apos;s blocking who in Postgres'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-116249307704338858</id><published>2006-11-02T10:11:00.000-08:00</published><updated>2006-11-10T09:59:03.476-08:00</updated><title type='text'>codecs.StreamRecoder example</title><content type='html'>The documentation for the (very useful) Python codecs module is quite terse in places. In particular I didn't find the &lt;a href="http://docs.python.org/lib/stream-recoder-objects.html"&gt;StreamRecoder docs&lt;/a&gt; especially helpful, and there are no examples. Basically what I needed was an on-the-fly conversion from a file encoded in Latin-1 (which makes a good catch-all when you aren't sure of the encoding) into UTF-8. I was using this file as input to the database, via psycopg2's &lt;tt&gt;copy_from()&lt;/tt&gt; function. &lt;tt&gt;copy_from()&lt;/tt&gt; requires a file-like object. To further complicate matters, the incoming file was gzip compressed, but that's not too important for this example.&lt;br /&gt; &lt;br /&gt;Given an input file-like object (infile), here is the incantation to recode it from Latin-1 to UTF-8.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;    recoder = codecs.StreamRecoder(infile, &lt;br /&gt;        codecs.getencoder('utf-8'), &lt;br /&gt;        codecs.getdecoder('utf-8'), &lt;br /&gt;        codecs.getreader('latin-1'), &lt;br /&gt;        codecs.getwriter('latin-1'))&lt;/code&gt;&lt;/pre&gt;The result &lt;tt&gt;recoder&lt;/tt&gt; is a file-like object which when read returns UTF-8 encoded strings. When written it accepts UTF-8, but the data is written to the file in Latin-1--though in my case I was only reading from the file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-116249307704338858?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/116249307704338858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=116249307704338858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/116249307704338858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/116249307704338858'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/11/codecsstreamrecoder-example.html' title='codecs.StreamRecoder example'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-115570809938250362</id><published>2006-08-15T22:29:00.000-07:00</published><updated>2006-08-15T23:22:44.340-07:00</updated><title type='text'>vimtabulous</title><content type='html'>One thing I've really missed since the days of having fluxbox as my window manager was tabbed windows. It was really useful for chaps like me who have tons of windows open and poor innate visual organization skills. Well vim 7 doesn't quite bring back those glory days (not that I miss XF86Config even a trifle), but it certainly greatly improves the experience with many files open.&lt;br /&gt;&lt;br /&gt;Tabs augment (but do not replace) the existing vim buffer model. Each tab is a separate window&amp;mdash;actually a separate page of windows if you must know&amp;mdash;thus each tab can, if desired, contain several windows and buffers. I'm not sure how useful that is, but it could be interesting if you like to split your windows to view multiple buffers simultaneously.&lt;br /&gt;&lt;br /&gt;Normally, if you launch vim and specify multiple files to edit on the command line, it puts all of the files into separate buffers. Now, you can tell vim, using the &lt;tt&gt;-p&lt;/tt&gt; option to put each file in it's own tab instead. The result is easier navigation between files. I've got vim aliased to &lt;tt&gt;vim -p&lt;/tt&gt; now so it uses tabs by default for multiple files.&lt;br /&gt;&lt;br /&gt;Once in vim, when you use the &lt;tt&gt;:e&lt;/tt&gt; command, it still opens a new buffer in the current window. To instead open the file in a new tab, use &lt;tt&gt;:tabe&lt;/tt&gt;. To navigate between tabs, you can use the mouse, or the &lt;tt&gt;:tabp&lt;/tt&gt; and &lt;tt&gt;:tabn&lt;/tt&gt; commands. I find those commands pretty tedious, so I've added the following key mappings to my &lt;tt&gt;.vimrc&lt;/tt&gt; file:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;"tab mappings &lt;br /&gt;nmap &amp;lt;D-1&amp;gt; 1gt &lt;br /&gt;nmap &amp;lt;D-2&amp;gt; 2gt &lt;br /&gt;nmap &amp;lt;D-3&amp;gt; 3gt &lt;br /&gt;nmap &amp;lt;D-4&amp;gt; 4gt &lt;br /&gt;nmap &amp;lt;D-5&amp;gt; 5gt &lt;br /&gt;nmap &amp;lt;D-6&amp;gt; 6gt &lt;br /&gt;nmap &amp;lt;D-7&amp;gt; 7gt &lt;br /&gt;nmap &amp;lt;D-8&amp;gt; 8gt &lt;br /&gt;nmap &amp;lt;D-9&amp;gt; 9gt &lt;br /&gt;nmap &amp;lt;D-t&amp;gt; :tabnew&amp;lt;CR&amp;gt; &lt;br /&gt;nmap &amp;lt;D-w&amp;gt; :tabclose&amp;lt;CR&amp;gt;&lt;br /&gt;nmap &amp;lt;C-D-\&amp;gt; :tabp&amp;lt;CR&amp;gt;&lt;br /&gt;nmap &amp;lt;C-D-]&amp;gt; :tabn&amp;lt;CR&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This gives me &lt;tt&gt;[cmd]+1&lt;/tt&gt; through &lt;tt&gt;[cmd]+9&lt;/tt&gt; to jump directly to a tab, &lt;tt&gt;[cmd]+t&lt;/tt&gt; to open a fresh tab, &lt;tt&gt;[cmd]+w&lt;/tt&gt; to close one and &lt;tt&gt;[cmd]+left&lt;/tt&gt; and &lt;tt&gt;[cmd]+right&lt;/tt&gt; to go to the previous and next tabs respectively. This makes navigating vim tabs more-or-less like iterm, consistency, it is good for ui!&lt;br /&gt;&lt;br /&gt;Here's a link with &lt;a href="http://www.vim.org/tips/tip.php?tip_id=1295"&gt;more ideas for key mappings&lt;/a&gt;. Also check out the &lt;a href="http://vimdoc.sourceforge.net/htmldoc/tabpage.html"&gt;full vim documentation on tabs&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-115570809938250362?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/115570809938250362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=115570809938250362' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/115570809938250362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/115570809938250362'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/08/vimtabulous.html' title='vimtabulous'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-115366592492691311</id><published>2006-07-23T07:31:00.000-07:00</published><updated>2006-07-23T08:08:18.483-07:00</updated><title type='text'>Work at home luxury</title><content type='html'>Ever since we moved into our new house, working from home -- which I do about one a week -- has been less than ideal. We had this big klunky desk which we got rid of before the move. For now, we have our iMac setup on a makeshift desk, but my laptop was, well, relegated to my lap. Anyone who has a MacBook knows firsthand how impractical that is. Let's just say aluminum is an excellent conductor of heat ;^).&lt;br /&gt;&lt;a href="http://www.csnofficefurniture.com/asp/show_detail.asp?sku=BL1330"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px;" src="http://common.csnstores.com/common/products/BL/BL1330_m.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;I looked at dozens of different desks and laptop stands online and locally. It's amazing the amount crappy, yet expensive, furniture that's out there. After far too much researching, I found this outfit &lt;a href="http://www.csnofficefurniture.com/"&gt;CSN Office Funiture&lt;/a&gt; which has an excellent select of good quality, inexpensive and simple desks; three qualities that seem to occur together infrequently (I mean, check out the stock photos on the home page!). As will all "site unseen" purchases, it was a bit of a leap of faith, but I ordered what looked to be an ideal laptop desk for me (I mean, check out the cup holder! ;^).&lt;br /&gt;&lt;br /&gt;It arrived on Friday and I put it together in about 15 minutes yesterday. It is sweet. The height adjusts pneumatically like a swivel chair via that round wheel petal at the bottom. You can adjust the angle of the laptop platform (which could double as a writing tablet or mini drafting table even). So it's basically exactly what I wanted, and I'm very happy with it. I also ordered a desk and chair from them as well, hopefully the experience will be just as good.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-115366592492691311?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/115366592492691311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=115366592492691311' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/115366592492691311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/115366592492691311'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/07/work-at-home-luxury.html' title='Work at home luxury'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-115346153179244916</id><published>2006-07-20T22:45:00.000-07:00</published><updated>2006-07-20T23:00:44.616-07:00</updated><title type='text'>Delete in Screen</title><content type='html'>screen is one of those essential utilities everyone (at least everyone that touches *nix) should have in their toolbelt. In true Unix tradition, the default key mapping for the &lt;tt&gt;[delete]&lt;/tt&gt; key is not what desktop users would normally expect. At least on Mac OS X, it acts as a "forward delete" when this key is typically the opposite.&lt;br /&gt;&lt;br /&gt;Anyhow after scouring around a bit I found the following incantation to add to my &lt;tt&gt;.screenrc&lt;/tt&gt; that makes the world safe for &lt;tt&gt;[delete]&lt;/tt&gt; in screen:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;bindkey -k kD stuff \177&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And there was much rejoicing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-115346153179244916?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/115346153179244916/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=115346153179244916' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/115346153179244916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/115346153179244916'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/07/delete-in-screen.html' title='Delete in Screen'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-114772501677734124</id><published>2006-05-15T13:12:00.000-07:00</published><updated>2006-05-15T13:34:18.373-07:00</updated><title type='text'>Darwinports from source</title><content type='html'>I've used &lt;a href="http://darwinports.opendarwin.org/"&gt;DarwinPorts&lt;/a&gt; for some time now (admittedly as a crutch from when I used FreeBSD as my desktop), but I hadn't installed anything new since I upgraded to my MacBook. It certainly has some rough edges, but it beats the heck out of tracking down the thirty dependencies for the latest software you want to install (in my case subversion).&lt;br /&gt;&lt;br /&gt;I prefer to put all my darwinports stuff in a separate directory tree from the rest of the system (~/opt), that way I don't have to build anything as root and I can avoid the unpleasantness of darwinports trashing system software and vice versa. Basically avoiding running anything as root is a good thing[TM] in my book. The old version of dp I had installed was not a universal binary, so I needed to upgrade it. dp can be downloaded as a disk image, but I couldn't find any way to install it in a different location. So, I just downloaded the source and tried to build it myself.&lt;br /&gt;&lt;br /&gt;Here's where the rough edges come in. dp configured just fine, but when I ran make I got the inexplicable error:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;===&gt; making install in config&lt;br /&gt;/bin/sh: line 1: cd: config: No such file or directory&lt;br /&gt;make: *** [install] Error 1&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now there was indeed a config directory sitting right there, so I rooted through the makefiles to find where it was trying to change to it, thinking it had changed directories somewhere else first or something. I found what I was looking for in the &lt;tt&gt;Mk/dports.subdir.mk&lt;/tt&gt;. I does your typical recursive make dance:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;for subdir in config doc src portmgr; do        echo ===\&gt; making all in $subdir;         ( cd $subdir &amp;&amp; make DIRPRFX=$subdir/ all) || exit 1; done&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I added some code above to echo the cwd and it showed it was in the right dir, but when I added &lt;tt&gt;cd config&lt;/tt&gt; it blew up. I tried a few different things to no avail, then I remembered seeing something similar on our make system here at work when trying to build things on the Mac. It had something to do with the bourne shell the Mac uses, though I didn't remember what. So I figured, "let's try bash":&lt;br /&gt;&lt;br /&gt;&lt;code&gt;make SHELL=/bin/bash &amp;&amp; sudo make install SHELL=/bin/bash&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And voila! It works. I still don't know what gives with &lt;tt&gt;/bin/sh&lt;/tt&gt;, but I'm off to the races...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-114772501677734124?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/114772501677734124/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=114772501677734124' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114772501677734124'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114772501677734124'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/05/darwinports-from-source.html' title='Darwinports from source'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-114612820161589970</id><published>2006-04-27T01:26:00.000-07:00</published><updated>2006-04-27T02:02:22.410-07:00</updated><title type='text'>Slony 1.1.5 subscribe set + autovacuum == deadlock</title><content type='html'>Slony has been pretty helpful in our migration to postgresql 8.1. Since you can run different versions of postgres on different nodes of the same cluster, we were able to bring up 8.1 on the primary and vet it for a few days while leaving the secondary on 7.4. That way if we ran into any showstoppers (hey, even with lots of QA, production can often throw you a curve), we could do a switchover and be back on 7.4 in a few minutes.&lt;br /&gt;&lt;br /&gt;Well, that scenario never came to pass, and now I am upgrading the secondary to 8.1 as well. After a rather slow experience reloading the primary after its upgrade I opted to rebuild the secondary rather than dumping it, reloading it and letting it sync.&lt;br /&gt;&lt;br /&gt;Everything was going swimmingly and the largest table was copied over when suddenly it caught a deadlock and aborted the load. Bummer. Troubleshooting deadlock errors are a bit like coming on an accident scene and trying to figure out what the drivers were thinking before the crash. Only in this case the crash has been rolled back, at least one of the vehicles has disappeared and all you have is a couple of terse notes to go on.&lt;br /&gt;&lt;br /&gt;Looking at the logs, I did see connections coming in from some other servers that I didn't expect. I decided to block all connections except local ones and those from the primary (via pg_hba.conf). Things were still trying to connect at regular intervals -- exactly what is a bit of a mystery to me, the secondary should only receive periodic connections at predictable times -- but now failing. This is when a bit of good luck came along: ironically in the form of another deadlock, this time much faster than before. I say this was good luck because I might well have waited another hour (yeah there's a lot of data) before it blew up.&lt;br /&gt;&lt;br /&gt;I was scratching my head trying to figure out what was connecting. The pg logs were not particularly helpful even though I log a good bit of detail about the clients for this purpose. It dawned on me that it was probably the autovacuum daemon making the rounds. Now, I had just recreated this same secondary a few days ago under 7.4 with autovacuum (same version of slony) and there was no deadlock. However I know for a fact that the old autovac config was much less aggressive than I have it configured in 8.1 (especially given the key vacuum throttling available now), so I imagine it just never found anything to do, or there was some other behavioral difference in the new version that bit me.&lt;br /&gt;&lt;br /&gt;So, long story short, I turned off autovac during the the secondary subscribe and so far so good, it hasn't fallen over. It's still chugging away on one of the big-ass tables, but here's hoping it comes around.&lt;br /&gt;&lt;br /&gt;Moral: Make sure nothing else can mess with the secondary while it subscribes. Disable outside connections and turn off autovacuum.&lt;br /&gt;&lt;br /&gt;In truth this whole problem is really a known bug in slony 1.1.5. I'm told that slony 1.2 grabs its locks early to block out everyone else at the get-go. Here's hoping that 1.2 hits prime-time soon, though I suspect being a feature release might make our integration longer to sort out. Nevertheless I'm happy to see so much effort going into Slony, it's a good reliable system, not without a gotcha or three, but well suited to its purpose.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-114612820161589970?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/114612820161589970/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=114612820161589970' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114612820161589970'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114612820161589970'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/04/slony-115-subscribe-set-autovacuum.html' title='Slony 1.1.5 subscribe set + autovacuum == deadlock'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-114490304943920509</id><published>2006-04-12T21:35:00.000-07:00</published><updated>2006-11-03T13:23:53.340-08:00</updated><title type='text'>vim 7.0 spellcheck</title><content type='html'>Here's a primer on using the new integrated spellcheck feature in vim 7.0:&lt;br /&gt;&lt;br /&gt;To turn on spell checking add this to your .vimrc:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;setlocal spell spelllang=en_us&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;or simply:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;set spell&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;which seems sufficient for me. I don't want it for all files, so I added it conditionally for text, html and README files:&lt;br /&gt;&lt;br /&gt;&lt;tt&gt;autocmd BufNewFile,BufRead *.txt,*.html,README set spell&lt;/tt&gt;&lt;br /&gt;&lt;br /&gt;Then create a directory ~/.vim/spell for vim to hold personal word lists.&lt;br /&gt;&lt;br /&gt;Once turned on here's a quick rundown of the basics (of course &lt;tt&gt;:help spell&lt;/tt&gt; is your friend):&lt;br /&gt;&lt;li&gt;use &lt;tt&gt;]s&lt;/tt&gt; and &lt;tt&gt;[s&lt;/tt&gt; to navigate to the next and previous misspelled words.&lt;br /&gt;&lt;li&gt;use &lt;tt&gt;z=&lt;/tt&gt; to suggest spelling for a misspelled word&lt;br /&gt;&lt;li&gt;use &lt;tt&gt;zg&lt;/tt&gt; to add words permanently to your personal word list. This marks it as a "good" word.&lt;br /&gt;&lt;li&gt;use &lt;tt&gt;zG&lt;/tt&gt; to temporarily add words for just this editing session.&lt;br /&gt;&lt;li&gt;use &lt;tt&gt;zw&lt;/tt&gt; to mark a word as misspelled.&lt;br /&gt;&lt;br /&gt;There are other shortcuts as well:&lt;br /&gt;&lt;br /&gt;&lt;li&gt;While in insert mode, use &lt;tt&gt;CTRL-x s&lt;/tt&gt; to suggest spelling for a word to the left of the cursor. You can then select a suggestion from the popup menu.&lt;br /&gt;&lt;li&gt;If you enter a common misspelling or mis-capitalization, try the &lt;tt&gt;1z=&lt;/tt&gt; command which replaces the word with the first spelling suggestion straight away. You can always undo it if it's not the right one.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-114490304943920509?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/114490304943920509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=114490304943920509' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114490304943920509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114490304943920509'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/04/vim-70-spellcheck.html' title='vim 7.0 spellcheck'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-114482418336836839</id><published>2006-04-11T22:33:00.000-07:00</published><updated>2006-04-12T22:29:13.870-07:00</updated><title type='text'>MacBook Pro, Yo</title><content type='html'>I've pretty much gotten the new MacBook setup to my liking. I must say, Apple really has it down in transferring from old to new machine. All of my stuff was seemlessly moved over from my "old" G4, barring some intel-incompatible apps. I had to install a new version of vim, but I found the 6.4.000 version would run but crash on save (ouch). So I installed the 7.0c2 beta. It doesn't crash, but there are a few bumps, specifically:&lt;br /&gt;&lt;br /&gt;&lt;li&gt;It doesn't seem to draw text quite right. Sometimes the text gets shifted over, when you cursor over it, the character it looks like you are on isn't the right one. The right one appears for a second, but then it shifts back again. Even refreshing the screen doesn't help. I guess I'll try another font, but if I have to give up &lt;a href="http://www.tobias-jung.de/seekingprofont/"&gt;ProFont&lt;/a&gt; I'm gonna be pissed ;^)&lt;br /&gt;&lt;li&gt;It doesn't seem to draw text quite the same way. I was using an anti-aliased ProFont before, but it draws too small now (though the line spacing looks right). In fairness, this may be an issue with Tiger and not vim (I was running 10.3 before)&lt;br /&gt;&lt;li&gt;When you use "change" commands (cw, ct, cf, etc), and you use backspace to delete from the right, the characters don't go away until you switch back to command mode. I've seen vim act like this in the past, so I suspect a change in a config option's default value&lt;br /&gt;&lt;br /&gt;On the bright side, 7.0 adds built-in spellcheck, and the new &lt;tt&gt;virtualedit=onemore&lt;/tt&gt; option removes an annoying vi artifact. &lt;tt&gt;onemore&lt;/tt&gt; allows you to place the cursor one character beyond the end of the line without entering insert mode using A for example. The undo branch functionality sounds amazing, if I understand it correctly, it will mean everything you enter will stick around somewhere, regardless of intervening redos/undos. I just installed it today, so there are bound to be other handy options to figure out tomorrow. If I can get the text to draw correctly, I'll be in business.&lt;br /&gt;&lt;br /&gt;There is still one thing that annoys me to no end with vim. Try the following: In command mode, place the cursor in the middle of a line of text. Then alternately enter insert and command mode using "i" and [esc]/[CTRL+C]. Notice how the cursor moves to the left each time. Now this doesn't happen if you enter insert mode using "a". Perhaps I'm just assbackward, but what I find is that I'm always one character off to the left from where I want to be when I leave insert mode. The &lt;tt&gt;onemore&lt;/tt&gt; option does help since it allows you to place the cursor "past" the end of the line. I'm sure there's a hack to take care of this and someday I'll find it (and post it here for posterity).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;update:&lt;/b&gt; I figured out how to get characters to go away right away when using change commands and backspace. I added this to my &lt;tt&gt;.vimrc&lt;/tt&gt; and all was well again:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;" Redraw immediately when replacing text within a line&lt;br /&gt;set cpoptions-=$&lt;br /&gt;" Erase backspaced chars immediately&lt;br /&gt;set cpoptions-=v&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-114482418336836839?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/114482418336836839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=114482418336836839' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114482418336836839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114482418336836839'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/04/macbook-pro-yo.html' title='MacBook Pro, Yo'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-114461572394974776</id><published>2006-04-09T13:44:00.000-07:00</published><updated>2006-04-09T13:48:43.950-07:00</updated><title type='text'>hacman</title><content type='html'>A tribute to this blog's namesake:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://users.erols.com/mowerman/pacfile.htm"&gt;http://users.erols.com/mowerman/pacfile.htm&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I especially dig the part about "Crazy Otto", the original (and bootleg) game that became Ms. Pacman&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-114461572394974776?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/114461572394974776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=114461572394974776' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114461572394974776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114461572394974776'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/04/hacman.html' title='hacman'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-25736244.post-114461375162054067</id><published>2006-04-09T13:11:00.000-07:00</published><updated>2006-04-13T17:27:37.720-07:00</updated><title type='text'>what's new in python 2.5</title><content type='html'>Andrew Kuchling has provided us with an overview of what to expect in python 2.5. It's not done yet, but there's some good stuff there anyhow:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://docs.python.org/dev/whatsnew/"&gt;http://docs.python.org/dev/whatsnew/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/25736244-114461375162054067?l=eatthedots.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://eatthedots.blogspot.com/feeds/114461375162054067/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=25736244&amp;postID=114461375162054067' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114461375162054067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/25736244/posts/default/114461375162054067'/><link rel='alternate' type='text/html' href='http://eatthedots.blogspot.com/2006/04/whats-new-in-python-25.html' title='what&apos;s new in python 2.5'/><author><name>casey</name><uri>http://www.blogger.com/profile/09131446473176959067</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
