unethical blogger - Python
http://unethicalblogger.com/taxonomy/term/19/0
Python-related postsenUnclog the tubes; blocking detection in Eventlet
http://unethicalblogger.com/posts/2010/08/unclog_tubes_blocking_detection_eventlet
<p>Colleagues of mine are all very familiar with my admiration of <a href="http://eventlet.net">Eventlet</a>, a
Python concurrency library, built on top of <a href="http://pypi.python.org/pypi/greenlet">greenlet</a>, that
provides lightweight "greenthreads" that naturally yield around I/O points. For me, the biggest draw of Eventlet
besides its maturity, is how well it integrates with standard Python code. Any code that uses the built-in
<code>socket</code> module can be "monkey-patched" (i.e. modified at runtime) to use the "green" version of the socket
module which allows Eventlet to turn regular ol' Python into code with asynchronous I/O.</p>
<p>The problem with using libraries like Eventlet, is that some Python code just <strong>blocks</strong>, meaning that
code will hit an I/O point and <em>not</em> yield but instead block the entire process until that network operation
completes.</p>
<p>In practical terms, imagine you have a web crawler that uses 10 "green threads", each crawling a
different site. The first greenthread (GT1) will send an HTTP request to the first site, then it will yield
to GT2 and so on. If each HTTP request blocks for 100ms, that means when crawling the 10 sites, you're going
to block the whole process, preventing anything from running, for a whole second. Doesn't sound too terrible,
but imagine you've got 1000 greenthreads, instead of everything smoothly yielding from one thread to another
the process will lock up very often resulting in painful slowdowns.</p>
<p>Starting with Eventlet 0.9.10 "blocking detection" code has been incorporated into Eventlet to make
it far easier for developers to find these portions of code that can block the entire process.</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">import</span> eventlet.<span style="color: black;">debug</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> eventlet.<span style="color: black;">debug</span>.<span style="color: black;">hub_blocking_detection</span><span style="color: black;">(</span><span style="color: #008000;">True</span><span style="color: black;">)</span></div></li></ol></pre></div>
<p>While using the blocking detection is fairly simple, its implementation is a bit "magical" in that
it's not entirely obvious how it works. The detector is built around signals, inside of Eventlet a signal
handler is set up prior to firing some code and then after said code has executed, if a certain time-threshhold
has passed, an alarm is raised dumping a stack trace to the console. I'm not entirely convinced I'm explaining this
appropriately so here's some pseudo-code:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">def</span> runloop<span style="color: black;">(</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #dc143c;">signal</span>.<span style="color: black;">alarm</span><span style="color: black;">(</span>handler, <span style="color: #ff4500;">1</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> execute_next_block<span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: black;">(</span><span style="color: #dc143c;">time</span>.<span style="color: #dc143c;">time</span><span style="color: black;">(</span><span style="color: black;">)</span> - start<span style="color: black;">)</span> <span style="color: #66cc66;"><</span> resolution:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> clear_signal<span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Clear the signal if we're less than a second, otherwise it will alarm</span></div></li></ol></pre></div>
<p>The blocking detection is a bit crude and can raise false positives if you have bits of code that churn
the CPU for longer than a second but it has been instrumental in incorporating <strong>non-blocking DNS</strong> support
into Eventlet, which was also introduced in 0.9.10 (ported over from Slide's <a href="http://github.com/slideinc/gogreen">gogreen</a>
package).</p>
<p>If you are using Eventlet, I highly recommend running your code periodically with blocking detection enabled,
it is an invaluable tool for determining whether you're running as fast and as asynchronous as possible. In my
case, it has been the difference between web services that are fast in development but slow under heavy stress,
and web services that are fast <strong>always</strong> regardless of load.</p>
http://unethicalblogger.com/posts/2010/08/unclog_tubes_blocking_detection_eventlet#commentsPythonSoftware DevelopmentSat, 28 Aug 2010 22:12:07 +0000R. Tyler Croy298 at http://unethicalblogger.comBeing a Libor, Addendum
http://unethicalblogger.com/posts/2010/05/being_libor_addendum
<p>A couple of weeks ago I wrote a post on how to "<a href="http://unethicalblogger.com/posts/2010/04/be_libor">Be a Libor</a>", trying to codify a few points I feel like I learned about building a successful engineering team at Slide. Shortly after the post went live, I discovered that Libor had been promoted to <a href="http://www.slide.com/corp/about-us.html">CTO at Slide</a>.</p>
<p>Over coffee today Libor offered up some finer points on the post in our discussion about building teams. It is important, according to Libor, to maintain a "mental framework" within which the stack fits; guiding decisions with a consistent world-view or ethos about building on top of the foundation laid. This is not to say that you should solve all problems with the same hammer, but rather if the standard operating procedure is to build small single-purpose utilities, you should not attack a new problem with a giant monolithic uber-application that does thirty different things (hyperbole alert!).</p>
<p>Libor also had a fantastic quote from the conversation with regards to approaching new problems:</p>
<blockquote>
<p>Just because there are multiple right answers, doesn't mean there's no wrong answers</p>
</blockquote>
<p>Depending on the complexity of the problems you're facing there are likely a number of solutions but you still can get it wrong, particularly if you don't remain consistent with your underlying mental framework for the project/organization.</p>
<p>As usual my discussions with Libor are interesting and enjoyable, he's one of the most capable, thoughtful engineers I know, so I'm interested to see the how Slide Engineering progresses under his careful hand as the new CTO. I hope you join me in wishing him the best of luck in his role, moving from wrangling coroutines, to herding cats.</p>
<p><a href="http://icanhascheezburger.com/2007/05/13/god-speed-moon-cat/">God speed mooncat</a></p>
http://unethicalblogger.com/posts/2010/05/being_libor_addendum#commentsAptureOpinionPythonSlideSoftware DevelopmentTue, 18 May 2010 16:00:00 +0000R. Tyler Croy286 at http://unethicalblogger.comIs programming with Twisted really as awful as it sounds?
http://unethicalblogger.com/posts/2010/05/programming_twisted_really_awful_it_sounds
<p>Early this week <a href="http://twitter.com/cansar">Can</a> forwarded <a href="http://www.quora.com/Is-programming-with-Twisted-really-as-awful-as-it-sounds">this post on Quora</a> to me, which asks the question:</p>
<blockquote>
<p>Is programming with Twisted really as awful as it sounds?</p>
</blockquote>
<p>Yes. <em>Yes</em>. <strong>YES IT IS</strong>. <strong><em>HOLY CRAP IT'S AWFUL</em></strong></p>
<p>Here's some good alternatives:</p>
<ul>
<li><a href="http://eventlet.net">Eventlet</a>, my preference</li>
<li><a href="http://gevent.org">gevent</a>, an alternative to Eventlet tied to libevent</li>
<li><a href="http://www.java.com/">Java</a>. because let's face it, if you're using Twisted, you've already decided not to write Python, so use something with proper threading support.</li>
</ul>
<p>That is all.</p>
http://unethicalblogger.com/posts/2010/05/programming_twisted_really_awful_it_sounds#commentsOpinionPythonWed, 12 May 2010 16:45:00 +0000R. Tyler Croy283 at http://unethicalblogger.comHow-to: Using Avro with Eventlet
http://unethicalblogger.com/posts/2010/05/howto_using_avro_eventlet
<p>Working on the plumbing behind a sufficiently large web application I find
myself building services to meet my needs more often than not. Typically I
try to build single-purpose services, following in the unix philosophy, cobbling
together more complex tools based on a collection of distinct building blocks.
In order to connect these services a solid, fast and easy-to-use RPC library is
a requirement; enter <a href="http://hadoop.apache.org/avro/">Avro</a>.</p>
<hr />
<p><em>Note:</em> You can skip ahead and just start reading some source code by cloning my
<a href="http://github.com/rtyler/eventlet-avro-example">eventlet-avro-example</a> repository
from GitHub.</p>
<hr />
<p>Avro is part of the Hadoop project and has two primary components, data serialization
and RPC support. Some time ago I chose Avro for serializing all of <a id="aptureLink_LDwxZTTwKh" href="http://www.apture.com">Apture's</a> metrics and logging
information, giving us a standardized framework for recording new events and processing
them after the fact. It was not until recently I started to take advantage of Avro's
RPC support when building services with <a id="aptureLink_a4wlc7Bdkp" href="http://eventlet.net/doc/">Eventlet</a>. I've talked about Eventlet <a href="http://unethicalblogger.com/posts/2010/01/new_years_python_meme">before</a>, but
to recap:</p>
<blockquote>
<p>Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it</p>
</blockquote>
<p>What this means in practice is that you can write highly concurrent network-based
services while keeping the code "synchronous" and easy to follow. Underneath
Eventlet is the "<a id="aptureLink_FICZSkfldQ" href="http://pypi.python.org/pypi/greenlet">greenlet</a>" library which implements coroutines for Python, which
allows Eventlet to switch between coroutines, or "green threads" whenever a network
call blocks.</p>
<p>Eventlet meets Avro RPC in an unlikely (in my opinion) place: WSGI. Instead of building
their own transport layer for RPC calls, Avro sits on top of HTTP for its transport
layer, POST'ing binary data to the server and processing the response. Since Avro can sit on top of HTTP, we can use <a href="http://eventlet.net/doc/modules/wsgi.html">eventlet.wsgi</a> for building a fast, simple RPC server.
<!--break--></p>
<h3>Defining the Protocol</h3>
<p>The first part of any Avro RPC project should be to define the protocol for RPC calls.
With Avro this entails a JSON-formatted specification, for our echo server example,
we have the following protocol:</p>
<pre><code>{"protocol" : "AvroEcho",
"namespace" : "rpc.sample.echo",
"doc" : "Protocol for our AVRO echo server",
"types" : [],
"messages" : {
"echo" : {
"doc" : "Echo the string back",
"request" : [
{"name" : "query", "type" : "string"}
],
"response" : "string",
"errors" : ["string"]
},
"split" : {
"doc" : "Split the string in two and echo",
"request" : [
{"name" : "query", "type" : "string"}
],
"response" : "string",
"errors" : ["string"]
}
}}
</code></pre>
<p>The protocol can be deconstructed into two concrete portions, type definitions and
a message enumeration. For our echo server we don't need any complex types, so the
<code>types</code> entry is empty. We do have two different messages defined, <code>echo</code> and <code>split</code>.
The message definition is a means of defining the actual remote-procedure-call,
services supporting this defined protocol will need to send responses for both kinds
of messages. For now, the messages are quite simple, they expect a <code>query</code> parameter
which should be a string, and are expected to return a string. Simple.</p>
<p>(This is defined in <a href="http://github.com/rtyler/eventlet-avro-example/blob/master/protocol.py">protocol.py</a> in the Git repo)</p>
<h3>Implementing a Client</h3>
<p>Implementing an Avro RPC client is simple, and the same whether you're building a
service with Eventlet or any other Python library so I won't dwell on the subject.
A client only needs to build two objects, an "HTTPTransceiver" which can be used
for multiple RPC calls and grafts additional logic on top of <code>httplib.HTTPConnection</code>
and a "Requestor".</p>
<pre><code>client = avro.ipc.HTTPTransceiver(HOST, PORT)
requestor = avro.ipc.Requestor(protocol.EchoProtocol, client)
response = requestor.request('echo', {'query' : 'Hello World'})
</code></pre>
<p>You can also re-use for same <code>Requestor</code> object for multiple messages of the same
protocol. The three-line snippet above will send an RPC message <code>echo</code> to the server
and then return the response.</p>
<p>(This is elaborated more on in <a href="http://github.com/rtyler/eventlet-avro-example/blob/master/client.py">client.py</a> in the Git repo)</p>
<h3>Building the server</h3>
<p>Building the server to service these Avro RPC messages is the most complicated
piece of the puzzle, but it's still remarkably simple. Inside the <code>server.py</code> you
will notice that we call <code>eventlet.monkey_patch()</code> at the top of the file. While not
strictly necessary inside the server since we're relying on <code>eventlet.wsgi</code>for
writing to the socket. Regardless it's a good habit to get into when working with
Eventlet, and would be required if our Avro-server was also an Avro-client, sending
requests to other services. Focusing on the simple use-case of returning responses
from the "echo" and "split" messages, first the WSGI server needs to be created:</p>
<pre><code>listener = eventlet.listen((HOST, PORT))
eventlet.wsgi.server(listener, wsgi_handler)
</code></pre>
<p>The <code>wsgi_handler</code> is a function which accepts the <code>environment</code> and <code>start_response</code>
arguments (per the WSGI "standard"). For the actually processing of the message,
you should refer to the <code>wsgi_handler</code> function in <code>server.py</code> in the example
repository.</p>
<pre><code>def wsgi_handler(env, start_response):
## Only allow POSTs, which is what Avro should be doing
if not env['REQUEST_METHOD'] == 'POST':
start_response('500 Error', [('Content-Type', 'text/plain')])
return ['Invalid REQUEST_METHOD\r\n']
## Pull the avro rpc message off of the POST data in `wsgi.input`
reader = avro.ipc.FramedReader(env['wsgi.input'])
request = reader.read_framed_message()
response = responder.respond(request)
## avro.ipc.FramedWriter really wants a file-like object to write out to
## but since we're in WSGI-land we'll write to a StringIO and then output the
## buffer in a "proper" WSGI manner
out = StringIO.StringIO()
writer = avro.ipc.FramedWriter(out)
writer.write_framed_message(response)
start_response('200 OK', [('Content-Type', 'avro/binary')])
return [out.getvalue()]
</code></pre>
<p>The only notable quirk with using Avro with a WSGI framework like
<code>eventlet.wsgi</code> is that some of Avro's "writer" code expects to be given a raw
socket to write a response to, so we give it a <code>StringIO</code> object to write to and
return that buffer's contents from <code>wsgi_handler</code>. The <code>wsgi_handler</code> function
above is "dumb" insofar that it's simply passing the Avro request object into the
"responder" which is responsible for doing the work:</p>
<pre><code>class EchoResponder(avro.ipc.Responder):
def invoke(self, message, request):
handler = 'handle_%s' % message.name
if not hasattr(self, handler):
raise Exception('I can\'t handle this message! (%s)' % message.name)
return getattr(self, handler)(message, request)
def handle_split(self, message, request):
query = request['query']
halfway = len(query) / 2
return query[:halfway]
def handle_echo(self, message, request):
return request['query']
</code></pre>
<p>All in all, minus comments the server code is around 40 lines and fairly easy to
follow (refer to <a href="http://github.com/rtyler/eventlet-avro-example/blob/master/server.py">server.py</a> for the complete version). I personally find Avro to be straight-forward enough and enjoyable to work with, being able to integrate it with my existing Eventlet-based stack is just icing on the cake after that.</p>
<p>If you're curious about some of the other work I've been up to with Eventlet, <a href="http://github.com/rtyler">follow me on GitHub</a> :)</p>
http://unethicalblogger.com/posts/2010/05/howto_using_avro_eventlet#commentsApturePythonSoftware DevelopmentFri, 07 May 2010 16:45:00 +0000R. Tyler Croy282 at http://unethicalblogger.comBe a Libor
http://unethicalblogger.com/posts/2010/04/be_libor
<p>I reflect occasionally on how I've gotten to where I am right now, specifically to how I made the jump from "just some kid at a Piggly Wiggly in Texas" as <a id="aptureLink_7fpgpX6rLb" href="http://twitter.com/stuffonfire">Dave</a> once said, to the guy who knows <em>stuff</em> about <strong>things</strong>. I often think about what pieces of the <a id="aptureLink_CJpdUZmrfu" href="http://twitter.com/slideinc">Slide</a> engineering environment were influential to my personal growth and how I can carry those forward to build as solid an engineering organization at <a id="aptureLink_jd3j6BSrUf" href="http://www.apture.com">Apture</a>.</p>
<p>The two pillars of engineering at Slide, at least in my naive world-view, were Dave and <a id="aptureLink_xrzzjPhkPZ" href="http://www.facebook.com/libor.michalek">Libor</a>. I joined Dave's team when I joined Slide, and I left Libor's team when I left Slide. Dave ran the client team, and did exceptionally well at filling a void that existed at Slide bridging engineering prowess with product management. Libor often furrowed his brow and built some of the large distributed systems that gave Slide an edge when dealing with incredible growth. In my first couple years I did my best to emulate Dave, engineers would always vie for Dave's time, asking questions and working through problems until they could return to their desk with the confidence that they understood the forces involved and solve the task at hand. Now that I'm at Apture, I'm trying to emulate Libor.</p>
<p>(<em>Note</em>: I do not intend to idolize either of them, but cite important characteristics)</p>
<p>To understand the Libor role, the phrase "the buck stops here" is useful. A Libor is the end of the line for engineering questions, unlike some organizations the "question-chain-of-command" is not the same as the org-chart. If a problem or question progressed up the stack to a Libor, and between an engineer and a Libor the pair cannot solve the problem, <em>you're screwed</em>.</p>
<p>What does it take to be a Libor you may be thinking:
<!--break-->
* <strong>No Guessing:</strong> When acting as a Libor, <em>knowing</em> is crucial. That is not to say you must understand everything about all the nooks and crannies of the code-base, but when you give an answer it is crucial you actually know what the hell you are talking about. The consequences of being wrong are far worst than the consequences of not knowing, if a fellow engineer builds on your guess, when that code ships live in a few days/weeks there is a serious risk of everything falling over.</p>
<ul>
<li><p><strong>Grok the stack:</strong> A Libor is expected to hold a wealth of information internally, much like a clock maker, a Libor should understand where every single gear and spring fit together in a large complex system. It is not necessary to understand how each component individually works but instead, understand how all the pieces operate in concert. Some amount of acting as a Libor requires direct discussions with the operations team as well as the rest of engineering, when all that JavaScript and Python rolls out to 10, 20, 100, or 1,000 machines, somebody should have at least considered the ramifications of adding 3 more database calls to every request, that's the Libor.</p></li>
<li><p><strong>Maintenance and accountability:</strong> Typically working at the lower ends of the stack, a Libor has to relive and tolerate last month's and last year's short-sighted decisions over and over. A Libor should not let himself nor colleagues "fire and forget" code, poor judgement will haunt a Libor for much longer than most people's New Year's resolutions. Because of this mistake-longevity, a Libor should be quite concerned with how well thought-out and tested new changes, particularly drastic ones, are.</p></li>
<li><p><strong>Focus on Engineering:</strong> Code quality and extendability are Libor's primary focus, that is not to say that a Libor's role is to impede product development, but rather ensure that it is properly framed. While a product manager's primary concern may be to get a feature deployed as soon as possible, the primary concern of a Libor is to ensure that once that feature is shipped it doesn't break or otherwise degrade the quality of service of the rest of the site. When interfacing with other engineers a Libor should be asking questions about code, intentions and implementation. Code review is as important as communication with the team, flatly rejecting code is unacceptable, but discussing with engineers the potential pitfalls of certain approaches ensures that the group moves forward.</p></li>
</ul>
<p>Playing the Libor character at Apture has been interesting to say the least, I've done a lot of work getting a number of systems in place to help educate my decisions, particularly in our production environment. Focusing on the entire stack as a complex system has allowed us to make some adjustments here and there that have literally started to pay dividends the day after they ship.</p>
<p>Non-engineering also benefits from having a Libor character in the organization, at Apture the product development narrative has changed, I find myself emphasizing:</p>
<blockquote>
<p>Tell me what you want, we'll find a way to do it</p>
</blockquote>
<p><em>That's</em> <a href="http://twitter.com/tristanharris/status/8355935929">a breakthrough</a>.</p>
http://unethicalblogger.com/posts/2010/04/be_libor#commentsAptureOpinionPythonSlideSoftware DevelopmentFri, 30 Apr 2010 14:45:00 +0000R. Tyler Croy281 at http://unethicalblogger.comPyrage: Static isn't just something on the radio
http://unethicalblogger.com/posts/2010/02/pyrage_static_isnt_just_something_radio
<p>Dealing with statics in Python is something that has bitten me enough times that I have become quite pedantic about them when I see them. I'm sure you're thinking "But Dr. Tyler, Python is a <em>dynamic</em> language!", it is indeed, but that does not mean there aren't static variables.</p>
<p>The funny thing about static variables in Python, in my opinion, once you understand a bit about scoping and what you're dealing with, it makes far more sense. Let's take this static class variable for example:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> <span style="color: #ff7700;font-weight:bold;">class</span> Foo<span style="color: black;">(</span><span style="color: #008000;">object</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">... <span style="color: black;">my_list</span> = <span style="color: black;">[</span><span style="color: black;">]</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">... </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> f = Foo<span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> b = Foo<span style="color: black;">(</span><span style="color: black;">)</span></div></li></ol></pre></div>
<p>You're trying to be clever, defining your class variables with their default variables outside of your <code>__init__</code> function, understandable, unless you ever intend on <strong>mutating</strong> that variable.</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> f.<span style="color: black;">my_list</span>.<span style="color: black;">append</span><span style="color: black;">(</span><span style="color: #483d8b;">'O HAI'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> <span style="color: #ff7700;font-weight:bold;">print</span> b.<span style="color: black;">my_list</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: black;">[</span><span style="color: #483d8b;">'O HAI'</span><span style="color: black;">]</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> </div></li></ol></pre></div>
<p>Still feeling clever? If that's what you <em>wanted</em>, I bet you do, but if you wanted each class to have its own internal list you've inadvertantly introduced a bug where <em>any</em> and <em>every</em> time something mutates <code>my_list</code>, it will change for every single instance of <code>Foo</code>. The reason that this occurs is because <code>my_list</code> is tied to the class object <code>Foo</code> and not the <strong>instance</strong> of the <code>Foo</code> object (<code>f</code> or <code>b</code>). In effect <code>f.__class__.my_list</code> and <code>b.__class__.my_list</code> are the same object, in fact, the <code>__class__</code> objects of both those instances is the same as well.</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> <span style="color: #008000;">id</span><span style="color: black;">(</span>f.__class__<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff4500;">7680112</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> <span style="color: #008000;">id</span><span style="color: black;">(</span>b.__class__<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #ff4500;">7680112</span></div></li></ol></pre></div>
<p><br clear="all"/>
When using default/optional parameters for methods you can also run afoul of statics in Python, for example:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> <span style="color: #ff7700;font-weight:bold;">def</span> somefunc<span style="color: black;">(</span>data=<span style="color: black;">[</span><span style="color: black;">]</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">... <span style="color: black;">data</span>.<span style="color: black;">append</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">... <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: black;">(</span><span style="color: #483d8b;">'data'</span>, data<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal">... </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> somefunc<span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: black;">(</span><span style="color: #483d8b;">'data'</span>, <span style="color: black;">[</span><span style="color: #ff4500;">1</span><span style="color: black;">]</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> somefunc<span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: black;">(</span><span style="color: #483d8b;">'data'</span>, <span style="color: black;">[</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">1</span><span style="color: black;">]</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> somefunc<span style="color: black;">(</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: black;">(</span><span style="color: #483d8b;">'data'</span>, <span style="color: black;">[</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">1</span><span style="color: black;">]</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #66cc66;">>>></span> </div></li></ol></pre></div>
<p>This comes down to a scoping issue as well, functions and methods in Python are first-class objects. In this case, you're adding the variable <code>data</code> to the <code>somefunc.func_defaults</code> tuple, which is being mutated when the function is being called. Bad programmer!</p>
<p>It all seems simple enough, but I still consistently see these mistakes in plenty of different Python projects (both pony-affiliated, and not). When these bugs strike they're difficult to spot, frustrating to deal with ("who the hell is changing my variable!") and most importantly, easily prevented with a little understanding of how Python scoping works.</p>
<p>PYRAGE!
<!--break--></p>
http://unethicalblogger.com/posts/2010/02/pyrage_static_isnt_just_something_radio#commentsOpinionPythonSoftware DevelopmentFri, 26 Feb 2010 13:45:00 +0000R. Tyler Croy272 at http://unethicalblogger.comIf you want a viral license, use the GPL
http://unethicalblogger.com/posts/2010/02/if_you_want_viral_license_use_gpl
<p>My "roots" in the open source community come from the BSD side of the open source spectrum, my first major introduction being involvement with <a id="aptureLink_Z6pelwUEYA" href="http://en.wikipedia.org/wiki/FreeBSD">FreeBSD</a> and <a id="aptureLink_ZXZxVq5WFh" href="http://en.wikipedia.org/wiki/OpenBSD">OpenBSD</a>. It is not surprising that my licensing preferences fall on the BSD (2 or 3 clause) or MIT licenses, the MIT license reading as follows:</p>
<blockquote><p>Copyright (c) [year] [copyright holders]
<p>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
<p>
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
<p>
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.</blockquote>
<p>I bring the subject up because I wanted to address a brief "kerfuffle" that occurred recently on the <a id="aptureLink_0mMM3DzSHh" href="http://eventlet.net/">Eventlet</a> mailing list with the maintainer of <a id="aptureLink_BWth7wZxHe" href="http://www.gevent.org/">gevent</a>, a fork/rewrite of Eventlet. Both projects are MIT licensed which gives anybody that would like to fork the source code of either project a great deal of leeway to hack about with the code, commercialize it, etc.
<!--break-->
<strong>Disclaimer</strong>: I personally am a fan of Eventlet, use it quite often, and have recently taking up maintaining Spawning, a WSGI server that supports multiple processes/threads, non-blocking I/O and graceful code reloading, built on top of Eventlet.</p>
<p>The "kerfuffle" occurred after Ryan, the maintainer of Eventlet, took a few good modules from gevent; <em>shocking</em> as it may seem, a developer working with liberally licensed code took liberally licensed code from a similar project. The issue that the maintainer of gevent took with the incorporation of his code was all about attribution:</p>
<blockquote>
<p>I don't mind you borrowing code from gevent, the license permits it. However, please make it clear where are you getting the code from.</p>
</blockquote>
<p>Upon first reading the email, I doubled over to the <a href="http://bitbucket.org/which_linden/eventlet/">eventlet source on Bitbucket</a>, checked the files that were incorporated into the codebase (<a href="http://bitbucket.org/which_linden/eventlet/src/tip/eventlet/timeout.py">timeout.py</a> and <a href="http://bitbucket.org/which_linden/eventlet/src/tip/eventlet/queue.py">queue.py</a>)and sure enough the copyright attributing the original author were still in tact, surely this is a non-issue?</p>
<p>Unfortunately not, license pedantry is an open source community past-time, right up their with drinking and shouting. When I replied mentioning that the copyrights were correctly in place, mentioning that both projects were MIT licensed so both constraints of the license were met, that is, the MIT license notice was included with the code. In essence the disagreement revolves around what the phrase "this permission notice shall be included" entail, my interpretation of the license is such that the MIT license itself shall be included, not the specific file with additions from one project to another; after sending off my mail, I received the following reply:</p>
<blockquote>
<p>Ok, it's acceptable to use one LICENSE file but only if the copyright notice from gevent is present unchanged.</p>
<p>That is, take the notice from here http://bitbucket.org/denis/gevent/src/tip/LICENSE (the line with the url), and put it into eventlet's LICENSE, on a separate line. (It's OK to add "Copyright (c) 2009-2010" before it to make it in line with others).</p>
<p>That would settle it.</p>
</blockquote>
<p>Slightly pedantic in my opinion, the MIT license enumerates a line for copyright holders which has been hijacked for other information that the maintainer of gevent would like to propagate, I don't necessarily agree, but this is a mailing list not a court of law, so I'll allow it. The thread continues:</p>
<blockquote>
<p>The license did not change. I've only updated the copyright notice to include the url of the project to protect against abusive borrowers, that's it.</p>
</blockquote>
<p>This is where I draw the line, go all in, plant my flag in the sand and other unnecessary metaphors. <strong>Abusive borrowers?</strong> Analyzing the semantics of the phrase alone makes my head hurt, I have a mental image of two old ladies wherein one says to the other: "may I borrow a cup of sugar, you horse-faced hunch-backed bucket of moron?" The rest of the email is full of similarly head-hurting quotes, for brevity I won't include them here (you can read the thread <a href="https://lists.secondlife.com/pipermail/eventletdev/2010-February/000731.html">in the archives</a>).</p>
<p>I'm simply dumbfounded by the ignorance of what the MIT license actually <em>means</em>, unlike the LGPL or the GPL license which were specifically drafted to protect against "abusive borrowers", <a id="aptureLink_grMTA0vhuq" href="http://lwn.net/Articles/51570/">such as Cisco</a>, the MIT license is so open it's <em>almost</em> public domain.</p>
<p>To a certain extent I can understand the emotions behind the thread on the mailing list, I don't agree with them. If you're seeking attribution past the copyright line in a header, than perhaps the "original" <a href="http://en.wikipedia.org/wiki/BSD_licenses#4-clause_license_.28original_.22BSD_License.22.29">4 clause BSD license</a> is for you, or perhaps the LGPL or GPL which give you more control over what happens to the source code you originate? If this is what you're after, the MIT license is the wrong license.</p>
http://unethicalblogger.com/posts/2010/02/if_you_want_viral_license_use_gpl#commentsOpinionPythonTue, 23 Feb 2010 14:45:00 +0000R. Tyler Croy270 at http://unethicalblogger.comSupporting Python 3 is a Ghetto
http://unethicalblogger.com/posts/2010/02/supporting_python_3_ghetto
<p>In my spurious free time I maintain a few Python modules (<a id="aptureLink_LvMqViext1" href="http://github.com/rtyler/py-yajl">py-yajl</a>, <a id="aptureLink_SEruJN7rBc" href="http://en.wikipedia.org/wiki/CheetahTemplate">Cheetah</a>, <a id="aptureLink_3HQW6OMHEx" href="http://github.com/rtyler/PyECC">PyECC</a>) and am semi-involved in a couple others (<a id="aptureLink_1I31I3RdtY" href="http://www.djangoproject.com/">Django</a>, <a id="aptureLink_7qs5LoY2eY" href="http://eventlet.net/">Eventlet</a>), only one of which properly supports Python 3. For the uninitiated, Python 3 is a backwards incompatible progression of the Python language and CPython implementation thereof, it's represented significant challenges for the Python community insofar that supporting Python 2.xx, which is in wide deployment, and Python 3.xx simultaneously is difficult.</p>
<p>As it stands now my primary development environment is Python 2.6 on Linux/amd64, which means I get to take advantage of some of the nice things that were added to Python 3 and then back-ported to Python 2.6/2.7. Regular readers know about my undying love for Hudson, a Java-based continuous integration server, which I use to test and build all of the Python projects that I work on. While working this weekend I noticed that one of my C-based projects (py-yajl) was failing to link properly on Python 2.4 and 2.5. It might be easy to cut-off support for Python 2.4, which was first released over <strong>four years</strong> ago, there are still a number of heavy users of 2.4 (such as <a id="aptureLink_k20Tw96O5B" href="http://www.crunchbase.com/company/slide">Slide</a>), in fact it's still the default <code>/usr/bin/python</code> on Red Hat Enterprise Linux 5. What makes this C-based module special, is that thanks to <a id="aptureLink_l6Vcy3ytZB" href="http://twitter.com/teepark">Travis</a>, it runs properly on Python 3.1 as well. Since the Python C-API has been <em>fairly</em> stable through the 2 series into Python 3, maintaining a C-based module that supports multiple versions of Python.</p>
<p>In this case, it's as easy as some simple pre-processor definitions:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#if PY_MAJOR_VERSION >= 3</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#define IS_PYTHON3</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#endif</span></div></li></ol></pre></div>
<p>Which I can use further down the line to modify the handling some of the minor internal changes for Python 3:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#ifdef IS_PYTHON3</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> result = _internal_decode<span style="color: black;">(</span><span style="color: black;">(</span>_YajlDecoder <span style="color: #66cc66;">*</span><span style="color: black;">)</span>decoder, PyBytes_AsString<span style="color: black;">(</span>bufferstring<span style="color: black;">)</span>,</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> PyBytes_Size<span style="color: black;">(</span>bufferstring<span style="color: black;">)</span><span style="color: black;">)</span><span style="color: #66cc66;">;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> Py_XDECREF<span style="color: black;">(</span>bufferstring<span style="color: black;">)</span><span style="color: #66cc66;">;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#else</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> result = _internal_decode<span style="color: black;">(</span><span style="color: black;">(</span>_YajlDecoder <span style="color: #66cc66;">*</span><span style="color: black;">)</span>decoder, PyString_AsString<span style="color: black;">(</span>buffer<span style="color: black;">)</span>,</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> PyString_Size<span style="color: black;">(</span>buffer<span style="color: black;">)</span><span style="color: black;">)</span><span style="color: #66cc66;">;</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"><span style="color: #808080; font-style: italic;">#endif </span></div></li></ol></pre></div>
<p>Not particularly <em>pretty</em> but it gets the job done, supporting all major versions of Python.</p>
<h3>Python on Python</h3>
<p>Writing modules in C is fun, can give you pretty good performance, but is not something you would want to do with a <strong>large</strong> package like Django (for example). Python is the language we all know and love to work with, a much more pleasant language to work with than C. If you build packages in pure Python, those packages have a much better chance running on top of IronPython or Jython, and the entire Python ecosystem is better for it.</p>
<p>A few weeks ago when I started to look deeper into the possibility of Cheetah support for Python 3, I found a process riddled with faults. First a disclaimer, Cheetah is almost <strong>ten years</strong> old; it's one of the oldest Python projects I can think of that's still chugging along. This translates into some <em>very</em> old looking code, most people who are new to the language aren't familiar with some of the ways the language has changed in the past five years, let alone ten.</p>
<p>The current means of supporting Python 3 with pure Python packages is as follows:</p>
<ol>
<li>Refactor the code enough such that <code>2to3</code> can process it</li>
<li>Run <a id="aptureLink_GtN83eZUU3" href="http://docs.python.org/library/2to3.html">2to3</a> over the codebase, with the <code>-w</code> option to literally write the changes to the files</li>
<li>Test your code on Python 3 (if it fails, go back to step 1)</li>
<li>Create a source tarball, post to <a id="aptureLink_lvET3CCrpS" href="http://pypi.python.org/">PyPI</a>, continue developing in Python 2.xx </li>
</ol>
<p>I'm hoping you spotted the same problem with this model that I did, due to the reliance on <code>2to3</code> you are now trapped into <strong>always</strong> developing Python targeting Python <strong>2</strong>. This model will never succeed in moving people to Python 3, regardless of what amazing improvements it contains (such as the Unladen Swallow work) because you cannot develop on a day-to-day basis with Python 3, it's a magic conversion tool away.</p>
<p>Unlike with a C module for Python, I cannot <code>#ifdef</code> certain segments of code in and out, which forces me to constantly use <code>2to3</code> <em>or</em> fork my code and maintain two separate branches of my project, duplicating the work for every change. With Python 2 sticking around on the scene for years to come (I don;t believe 2.7 will be the last release) I cannot imagine <strong>either</strong> of these workflows making sense long term.</p>
<p>At a fundamental level, supporting Python 3 does not make sense for anybody developing modules, particularly open source ones. Despite Python 3 being "the future", it is currently impossible to develop using Python 3, maintaining support for Python 2, which <strong>all</strong> of us have to do. With enterprise operating systems like <a id="aptureLink_ehh7mOge8i" href="http://www.crunchbase.com/product/red-hat-enterprise-linux">Red Hat</a> or <a id="aptureLink_CklLBYgoAK" href="http://www.novell.com/linux/">SuSE</a> only now starting to get on board with Python 2.5 and Python 2.6, you can be certain that we're more than five years away from seeing Python 3 installed by default on any production machines.
<!--break--></p>
http://unethicalblogger.com/posts/2010/02/supporting_python_3_ghetto#commentsCheetahPythonSoftware DevelopmentSun, 21 Feb 2010 23:02:28 +0000R. Tyler Croy269 at http://unethicalblogger.comBuilding a game for ET. Day 1 with Pygame
http://unethicalblogger.com/posts/2010/02/building_game_et_day_1_pygame
<p>Earlier this week I was checking out <a id="aptureLink_8JIIdIavnW" href="http://en.wikipedia.org/wiki/Pygame">Pygame</a>, pondering what I could possibly build with it that could keep me motivated enough to finish it. Motivation would like be the primary problem for me with any amount of game programming; I'm not a gamer, I don't harbor a dislike of games, they're just not something I typically spend time playing (I do like to play "haggard late night open-source hacker" though, that's a fun one). Friday night I stumbled across an idea, ET likes to play (casual) games, perhaps we could write a game together; ask any engineer at EA or Ubisoft, there's nothing more romantic than working on a game.</p>
<p>Talking over the idea with ET on the ride home from the office, we talked about creating a typing-oriented game and started to brainstorm. The tricky aspect of a typing-oriented game is you have to walk the fine line of "educational gaming", that is to say, the game's goal is <strong>not</strong> to teach the player how to type. That sucks. Contrasted to some other games where the means of progressing in some games is by solving a puzzle, killing noobs in others, in this game we wanted the player to progress through levels/situations with their typing ability (ET finds this fun, we do not have this in common).</p>
<p>Over pizza we discussed more about how the levels would work, I decided that I wanted to use stories/articles instead of random words for the "content" of the game. We settled on a couple fundamental concepts: the player would earn coins by correctly completing a words as the scrolled from right to left (similar to a ticker tape), they would lose coins if they made a mistake or could not keep up. After a player completed a level (i.e. a "story") they would find themselves in a "store" of sorts, where they could purchase "tools" for future levels with their coins. The tools we decided would be a <em>very</em> important, as the player reached their upper bound of typing speed the utility of these various tools would necessary as a means of strategically conquering the level. One of the few things we didn't particularly cover was the "end game", whether the player would simply play increasingly more difficult levels (a la <a id="aptureLink_S4eypmdIVi" href="http://en.wikipedia.org/wiki/Tetris">Tetris</a>) or if they could actually "beat" the game. With at least the basics of the concept sketched out, it was time to start writing <em>some</em> code.</p>
<h3>Starting with Pygame</h3>
<p>It's <strong>incredibly</strong> important to mention that I've <em>never</em> programmed a game before. <em>Never-ever</em>. From my work with network programming I was already familiar with the concept of the run-loop that's pretty core to Pygame, but I had never really made use of any to animate objects on the screen or deal with handling any kind of events from mouse movements to key presses, etc. Fortunately I'm already a professional Python developer, so writing code wasn't the difficult part so much as laying it out. Orienting things into classes to handle separate components such as animating text (which is a painful in Pygame, in my opinion) to keeping track of user-input.</p>
<p>Animating text across the screen wasn't particularly difficult, with Pygame you first create your primary "surface" (i.e. the window) and then you can render things onto that surface. With text, you end up rendering a surface which contains your text, "hello world" which you then place onto the primary surface. Easy peasy thus far:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">import</span> pygame</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> surface = pygame.<span style="color: black;">display</span>.<span style="color: black;">set_mode</span><span style="color: black;">(</span><span style="color: black;">(</span><span style="color: #ff4500;">640</span>, <span style="color: #ff4500;">680</span><span style="color: black;">)</span>, pygame.<span style="color: black;">HWSURFACE</span> | pygame.<span style="color: black;">DOUBLEBUF</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> font = pygame.<span style="color: black;">font</span>.<span style="color: black;">SysFont</span><span style="color: black;">(</span><span style="color: #483d8b;">'Courier'</span>, <span style="color: #ff4500;">42</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">### render(text, antialias, rgb color tuple</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> font_surface = font.<span style="color: black;">render</span><span style="color: black;">(</span><span style="color: #483d8b;">'hello world'</span>, <span style="color: #ff4500;">0</span>, <span style="color: black;">(</span><span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span><span style="color: black;">)</span> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">### draw `font_surface` onto `surface` at (x=0, y=0)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> start_x, start_y = <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> surface.<span style="color: black;">blit</span><span style="color: black;">(</span>font_surface, <span style="color: black;">(</span>start_x, start_y<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">### Holy runloop batman</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> pygame.<span style="color: black;">display</span>.<span style="color: black;">update</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li></ol></pre></div>
<p>That was fun, I now have "hello world" rendered onto my screen, now to animate I suppose I'll just render <code>font_surface</code> a little further right every iteration of the runloop, i.e.</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">### Holy runloop batman</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> surface.<span style="color: black;">blit</span><span style="color: black;">(</span>font_surface, <span style="color: black;">(</span>start_x, start_y<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> start_x += <span style="color: #ff4500;">0.5</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> pygame.<span style="color: black;">display</span>.<span style="color: black;">update</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li></ol></pre></div>
<p>This blurs the text however, so I then changed to:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">### Holy runloop batman</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> surface.<span style="color: black;">blit</span><span style="color: black;">(</span>font_surface, <span style="color: black;">(</span>start_x, start_y<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> surface.<span style="color: black;">fill</span><span style="color: black;">(</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> start_x += <span style="color: #ff4500;">0.5</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> pygame.<span style="color: black;">display</span>.<span style="color: black;">update</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li></ol></pre></div>
<p>This will cause the (primary) surface to be repainted (washed over) every iteration of the runloop ensuring that the text will properly animate, drawing the text in one spot, wiping the surface then drawing it slightly further to the left, resulting in the scrolling animation. All's fine and good until you determine that you want to have <em>other</em> elements on the screen and you also don't want to redraw them every time around the carousel. I then discovered how to "fill" just one particular rectangle on the surface, i.e. the rectangle behind the text:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> text_w, text_h = font.<span style="color: black;">size</span><span style="color: black;">(</span><span style="color: #483d8b;">'hello world'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #008000;">True</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">### Holy runloop batman</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> surface.<span style="color: black;">blit</span><span style="color: black;">(</span>font_surface, <span style="color: black;">(</span>start_x, start_y<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> surface.<span style="color: black;">fill</span><span style="color: black;">(</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span>, rect=pygame.<span style="color: black;">Rect</span><span style="color: black;">(</span>start_x, start_y, text_w, text_h<span style="color: black;">)</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> start_x += <span style="color: #ff4500;">0.5</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> pygame.<span style="color: black;">display</span>.<span style="color: black;">update</span><span style="color: black;">(</span><span style="color: black;">)</span></div></li></ol></pre></div>
<p>Once I was able to get text properly scrolling across the screen, the rest of the afternoon of hacking was far easier. My confidence in my ability to grok Pygame in order to do what I wanted. I then set forth organizing my code into some logical classes, for example I created a <code>LetterSpool</code> class which would record the user's progress through the current word, rendering it at the bottom-center of the screen and firing an event when the user hit the space bar (denoting the word "complete"), additionally I wrapped my text animation code into <code>AnimatedWord</code> so I could easily string words together to scroll across the screen in conjunction similar to a textual screensaver.</p>
<p>Not a whole lot more to write about with regards to my progress today, hooked up some music and basic sound effects which was trivial after looking at some sample code. Next I need to start addressing some more fundamentals for user-interaction: scoring and level-changing.</p>
<p>You can track the progress of the game "Typy" (pronounced: <code>typey</code>) <a href="http://github.com/rtyler/typy">on GitHub</a></p>
<p><center><img src="http://agentdero.cachefly.net/scratch/typy_day1.png"/></center></p>
http://unethicalblogger.com/posts/2010/02/building_game_et_day_1_pygame#commentsPythonSun, 14 Feb 2010 09:35:24 +0000R. Tyler Croy266 at http://unethicalblogger.comThread-safety assumptions in Django
http://unethicalblogger.com/posts/2010/01/threadsafety_assumptions_django
<p>These days, the majority of my day job revolves around working with <a id="aptureLink_jvAxf3Xyiw" href="http://www.crunchbase.com/company/apture">Apture's</a> <a id="aptureLink_eYCk1i8kej" href="http://www.djangoproject.com/">Django</a>-based code which, depending on the situation, can be a blessing or a curse. In some of my recent work to help improve our ability to scale effectively, I started swapping out <a id="aptureLink_ybzn7lvyyE" href="http://en.wikipedia.org/wiki/Apache%20HTTP%20Server">Apache</a> for <a id="aptureLink_jDx5yFnmAS" href="http://pypi.python.org/pypi/Spawning">Spawning</a> web servers which can more efficiently handle large numbers of concurrent requests. One of the mechanisms by which Spawning accomplishes this task, is by using <a id="aptureLink_hJSBTiL356" href="http://eventlet.net/doc/">eventlet's</a> <code>tpool</code> (thread pool) module in addition to some other clever tricks. With Apache, we used pre-forked workers to accomplish the work needed to be done and while still using forked child processes with Spawning, threading was also thrown into the mix, that's when "shit got real" (so to speak).</p>
<p>We started seeing sporadic, difficult to reproduce errors. Not a lot, a trickle of exception emails throughout the day. Digging deeper into some of the exceptions, careful stepping through Apture code, into Django code and back again, I started to realize I had <strong>thread-safety problems</strong>. Shock! Panic! Despair! Lunch! Disappointment! Shock! I felt all these things and more. I've long lamented the number of globals used in Django's code base but this is the icing on the cake.</p>
<p>Apparently Django's <a href="http://code.djangoproject.com/wiki/DjangoSpecifications/Core/Threading">threading problems</a> are sufficiently documented in a <a href="http://y-node.com/blog/2008/oct/30/noreversematch/">few places</a>. Using a slightly older version of the Django framework certainly doesn't help but it doesn't <em>appear</em> that recent releases (1.1.1) can guarantee thread-safety anyways. I think it's safe to assume the majority of Django framework users are not using threaded web servers in any capacity, else this would have become a far larger issue (and hopefully of been fixed) by now. From <code>NoReverseMatch</code> exceptions, to curious middleware problems to thread-safety <a href="http://code.djangoproject.com/ticket/11193">issues</a> in the WSGI support layer, Django has potholes lying all along the road to multithreadedness.</p>
<p>Beware.
<!--break--></p>
http://unethicalblogger.com/posts/2010/01/threadsafety_assumptions_django#commentsOpinionPythonSoftware DevelopmentTue, 19 Jan 2010 05:23:26 +0000R. Tyler Croy259 at http://unethicalblogger.comVirtual Hosting with HAProxy and WSGI
http://unethicalblogger.com/posts/2010/01/virtual_hosting_haproxy_and_wsgi
<p>Lately I've fallen in love with a couple of fairly simple but powerful
technologies: <a id="aptureLink_MG9e1mBPnu" href="http://haproxy.1wt.eu/">haproxy</a> and <a id="aptureLink_h4s21gIvSE" href="http://en.wikipedia.org/wiki/Web%20Server%20Gateway%20Interface">WSGI</a> (web server gateway interface). While the latter
is more of a specification (<a id="aptureLink_J39ynRlO1s" href="http://en.wikipedia.org/wiki/Wsgi">PEP 333</a>) the concepts it puts forth have made my life
significantly easier. In combination, the two of them make for a powerful combination
for serving web applications of all kinds and colors.</p>
<p>HAProxy is a robust, reliable piece of load balancing software that's <strong>very</strong> easy
to get started with, For the uninitiated, load balancing is a common means of distributing
the load of a number of inbound requests across a pool of processes, machines, clusters and so on.
Whenever you hit any web site of non-trivial size, your HTTP requests are invariably transparently
proxied through a load balancer to a pool of web machines.</p>
<p>I started looking into haproxy when I began to move <a href="http://urlenco.de">Urlenco.de</a>
away from my franken-setup of <a id="aptureLink_JfNVXqw8zi" href="http://en.wikipedia.org/wiki/Lighttpd">Lighttpd</a>/<a id="aptureLink_VtVTJkexMb" href="http://en.wikipedia.org/wiki/FastCGI">FastCGI</a>/<a id="aptureLink_M8XmGBHeCs" href="http://en.wikipedia.org/wiki/Mono%20%28software%29">Mono</a>/<a id="aptureLink_vg9xXC8F19" href="http://www.asp.net/">ASP.NET</a> to a pure <a id="aptureLink_RkZQSvmVt3" href="http://en.wikipedia.org/wiki/Python%20%28programming%20language%29">Python</a> stack.
After poking around some articles about haproxy I discovered it can be used for <strong>virtual hosts</strong>
as well as simple load balancing. Using a haproxy's ACLs feature (see Section 7 in the
<a href="http://haproxy.1wt.eu/download/1.4/doc/configuration.txt">configuration.txt</a>), you can
redirect requests to one backend or another. While my "virtual hosting" with haproxy is using
the ability to inspect the HTTP headers of inbound requests, you can use a number of different
criterion to determine the right backend for serving a request: url matching, request method matching
(GET/POST), protocol matching (haproxy can load balance any kind of TCP connection) and so on.</p>
<p>WSGI (pronounced: <em>whiskey</em>) comes into play on the backend side of haproxy, using the
<a id="aptureLink_2I1tbDf9Uh" href="http://eventlet.net/doc/modules/wsgi.html">eventlet.wsgi</a> module which provides a WSGI interface I can build web applications <strong>very</strong>
quickly, test them and deploy them. When deployed, I can run them as "nobody" in userspace on
the server, binding to some higher numbered port (i.e. 8080) and haproxy will do the work routing
to the appropriate WSGI process.</p>
<p>Below is a simple haproxy configuration that I'm using to run <a href="http://urlenco.de">Urlenco.de</a> and
a site for <a href="http://erinandtylerswedding.com">my wedding</a> and many more as soon as I finish them. The section to note is <code>frontend http-in</code> in which the ACLs are defined for the different virtually hosted domains and the conditionals for selecting a backend based on those ACLs.</p>
<pre><code>global
maxconn 20000
ulimit-n 16384
log 127.0.0.1 local0
uid 200
gid 200
chroot /var/empty
nbproc 4
daemon
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
maxconn 2000
contimeout 5000
clitimeout 50000
srvtimeout 50000
frontend http-in
bind *:80
acl is_urlencode hdr_end(host) -i urlenco.de
acl is_wedding hdr_end(host) -i erinandtylerswedding.com
use_backend urlencode if is_urlencode
use_backend wedding if is_wedding
default_backend urlencode
backend urlencode
balance roundrobin
cookie SERVERID insert nocache indirect
option httpchk HEAD /check.txt HTTP/1.0
option httpclose
option forwardfor
server Local 127.0.0.1:8181 cookie Local
backend wedding
balance roundrobin
cookie SERVERID insert nocache indirect
option httpchk HEAD /check.txt HTTP/1.0
option httpclose
option forwardfor
server Local 127.0.0.1:8081 cookie Local
</code></pre>
http://unethicalblogger.com/posts/2010/01/virtual_hosting_haproxy_and_wsgi#commentsLinuxPythonSoftware DevelopmentSun, 17 Jan 2010 00:29:38 +0000R. Tyler Croy258 at http://unethicalblogger.comNew Year's Python Meme
http://unethicalblogger.com/posts/2010/01/new_years_python_meme
<p>While I'm not aggregated into the <a href="http://planet.python.org">Python Planet</a> I wanted to join in the <a href="http://just-another.net/2009/12/28/new-years-python-meme/">meme</a> <a href="http://blog.tplus1.com/index.php/2010/01/04/new-years-python-meme/">that's</a> <a href="http://tarekziade.wordpress.com/2009/12/28/new-years-python-meme/">already</a> <a href="http://coreygoldberg.blogspot.com/2009/12/new-years-python-meme.html">going</a> <a href="http://www.protocolostomy.com/2009/12/29/2009-python-meme/">on</a>.</p>
<h3>What’s the coolest Python application, framework or library you have discovered in 2009?</h3>
<p>While I didn't discover it until the latter half of 2009, I'd have to say <a href="http://eventlet.net">eventlet</a> is the coolest Python library I discovered in 2009. After leaving Slide, where I learned the joys of coroutines (a concept previously foreign to me) I briefly contemplated using greenlet to write a coroutines library similar to what is used at Slide. Fortunately I stumbled across eventlet in time, which shares common ancestry with Slide's proprietary library.</p>
<h3>What new programming technique did you learn in 2009?</h3>
<p>I'm not sure I really learned any new techniques over the past year, I started writing a <em>lot</em> more tests this past year but my habits don't quite qualify as Test Driven Development just yet. As far as Python goes, I've been introduced to the Python C API over the past year (written two entire modules in C <a href="http://github.com/rtyler/PyECC">PyECC</a> and <a href="http://github.com/rtyler/py-yajl">py-yajl</a>) and while I wouldn't exactly call implementing Python modules in C a "technique" it's certainly a departure from regular Python (<code>Py_XDECREF</code> I'm looking at you)</p>
<h3>What’s the name of the open source project you contributed the most in 2009? What did you do?</h3>
<p>Regular readers of my blog can likely guess which open source project I contributed to most in 2009, <a href="http://cheetahtemplate.org">Cheetah</a>, of which I've become the maintainer. I also authored a number of new Python projects in 2009: <a href="http://github.com/rtyler/PyECC">PyECC</a> a module implementing Elliptical Curve Cryptography (built on top of <a href="http://point-at-infinity.org/seccure/">seccure</a>), <a href="http://github.com/rtyler/py-yajl">py-yajl</a> a module utilizing <a href="http://lloyd.github.com/yajl">yajl</a> for fast JSON encoding/decoding, <a href="http://github.com/rtyler/IronWatin">IronWatin</a> an IronPython-based module for writing <a href="http://watin.sourceforge.net/">WatiN</a> tests in Python (supporting screengrabs as well), <a href="http://github.com/rtyler/PILServ">PILServ</a> an eventlet-based server to do server-side image transformations with <a href="http://www.pythonware.com/products/pil/">PIL</a>, <a href="http://github.com/rtyler/TweepyDeck">TweepyDeck</a> a PyGTK+ based Twitter client and <a href="http://github.com/rtyler/MicroMVC">MicroMVC</a> a teeny-tiny MVC styled framework for Python and WSGI built on eventlet and Cheetah.</p>
<h3>What was the Python blog or website you read the most in 2009?</h3>
<p>The <a href="http://reddit.com/r/python">Python reddit</a> was probably the most read Python-related "blog" I read in 2009, it generally supercedes the <a href="http://planet.python.org">Python Planet</a> with regards to content but also includes discussions as well as package release posts.</p>
<h3>What are the top three things you want to learn in 2010?</h3>
<ul>
<li><strong>Python 3</strong>. After spending a couple weekends trying to get Cheetah into good working order on Python 3, I must say, maintaining a Python-based module on both Python 2.xx and 3.xx really feels like a nightmare. py-yajl on the otherhand, being entirely C, was <strong>trivial</strong> to get compiling and executing perfectly for 2.xx and 3.xx</li>
<li><strong>NoSQL</strong>. Earlier this very evening I dumped a boatload of data out of PostgreSQL into <a href="http://code.google.com/p/redis/">Redis</a> and the resulting Python code for data access using <a href="http://github.com/andymccurdy/redis-py">redis-py</a> is shockingly simple. I'm looking forward to finding more places where a relational database is overkill for certain types of stored data, and using Redis instead.</li>
<li><strong>Optimizing Python</strong>. With py-yajl <a href="http://github.com/lloyd">Lloyd</a> and I had some fun optimizing the C code behind the module, but I'd love to learn some handy tricks to making pure-Python execute as fast as possible.</li>
</ul>
http://unethicalblogger.com/posts/2010/01/new_years_python_meme#commentsPythonMon, 04 Jan 2010 09:28:36 +0000R. Tyler Croy255 at http://unethicalblogger.comUsing Cheetah templates with Django
http://unethicalblogger.com/posts/2009/12/using_cheetah_templates_django
<p>Some time ago after reading a post on <a href="http://www.eflorenzano.com/blog/post/cheetah-and-django/">Eric Florenzano's blog</a> about hacking together support for <a id="aptureLink_OfHfDIpuSN" href="http://en.wikipedia.org/wiki/CheetahTemplate">Cheetah</a> with <a id="aptureLink_0oRd4dQsSK" href="http://en.wikipedia.org/wiki/Django%20%28web%20framework%29">Django</a>, I decided to add "proper" support for Cheetah/Django to Cheetah v2.2.1 (released June 1st, 2009). At the time I didn't use Django for anything, so I didn't really think about it too much more.</p>
<p>Now that I work at <a id="aptureLink_AYRRV0XTwi" href="http://www.crunchbase.com/company/apture">Apture</a>, which uses Django as part of its stack, Cheetah and Django playing nicely together is more attractive to me and as such I wanted to jot down a quick example project for others to use for getting started with Cheetah and Django. You can find the <a href="http://github.com/rtyler/django_cheetah_example">django_cheetah_example</a> project on GitHub, but the gist of how this works is as follows.</p>
<h3>Requires</h3>
<ul>
<li><a href="http://www.djangoproject.com/">Django</a></li>
<li><a href="http://cheetahtemplate.org">Cheetah</a> (>= v2.2.1)</li>
</ul>
<h3>Getting Started</h3>
<p>For all intents and purposes, using Cheetah in place of Django's
templating system is a trivial change in how you write your <em>views</em>.</p>
<p>After following the Django <a href="http://docs.djangoproject.com/en/1.1/intro/tutorial01/">getting started</a>
documentation, you'll want to create a directory for your Cheetah templates, such
as <code>Cheetar/templates</code>. Be sure to <code>touch __init__.py</code> in your template
directory to ensure that templates can be imported if they need to.</p>
<p>Add your new template directory to the <code>TEMPLATE_DIRS</code> attribute
in your project's <code>settings.py</code>.</p>
<p>Once that is all set up, utilizing Cheetah templates in Django is just
a matter of a few lines in your view code:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">import</span> Cheetah.<span style="color: black;">Django</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">def</span> index<span style="color: black;">(</span>req<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">return</span> Cheetah.<span style="color: black;">Django</span>.<span style="color: black;">render</span><span style="color: black;">(</span><span style="color: #483d8b;">'index.tmpl'</span>, greet=<span style="color: #008000;">False</span><span style="color: black;">)</span></div></li></ol></pre></div>
<p><strong>Note</strong>: Any keyword-arguments you pass into the <code>Cheetah.Django.render()</code>
function will be exposed in the template's "searchList", meaning you can
then access them with $-placeholders. (i.e. <code>$greet</code>)</p>
<p>With the current release of Cheetah (<a href="http://pypi.python.org/pypi/Cheetah/2.4.1">v2.4.1</a>), there isn't support for using pre-compiled Cheetah templates with Django (it'd be trivial to put together though) which means <code>Cheetah.Django.render()</code> uses Cheetah's dynamic compilation mode which can add a bit of overhead since templates are compiled at runtime (your mileage may vary).
<!--break--></p>
http://unethicalblogger.com/posts/2009/12/using_cheetah_templates_django#commentsCheetahPythonSoftware DevelopmentSat, 26 Dec 2009 20:31:11 +0000R. Tyler Croy247 at http://unethicalblogger.comPyrage: from toolbox import hammer
http://unethicalblogger.com/posts/2009/12/pyrage_toolbox_import_hammer
<p>Those that have worked with my directly know I'm a <em>tad</em>
obsessive when it comes to imports in <a id="aptureLink_leGNqOLSuI" href="http://en.wikipedia.org/wiki/Python%20%28programming%20language%29">Python</a>. Once upon a
time I had to write some pretty disgusting import hooks
to solve a problem and got to learn first-hand how gnarly
Python's import subsystem can be. I have a couple coding
conventions that I follow when I'm writing Python for my
own personal projects that typically follows:</p>
<ul>
<li>"strict" system imports first (i.e. <code>import time</code>) </li>
<li>"from" system imports second (i.e. <code>from eventlet import api</code>)</li>
<li>"local" imports (<code>import mymodule</code>)</li>
<li>local "from" imports (<code>from mypackage import module</code>)</li>
</ul>
<p>In all of these sections, I like to list things alphabetically
as well, just to make sure that at no point are modules ever
doubley-imported. This results in code that looks clean (in
my humblest of opinions):</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">#!/usr/bin/env python</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">from</span> eventlet <span style="color: #ff7700;font-weight:bold;">import</span> api</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">import</span> app.<span style="color: black;">util</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">from</span> app.<span style="color: black;">models</span> <span style="color: #ff7700;font-weight:bold;">import</span> account</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">## Etc.</span></div></li></ol></pre></div>
<p>A module importing habit that absolutely drives me up the wall,
I was introduced to and told "don't-do-that" by <a id="aptureLink_9aD3KAbJCx" href="http://twitter.com/stuffonfire">Dave</a>: importing
symbols from modules; in effect: <code>from MySQLdb import IntegrityError</code>.
I have two major reasons for hating the importing of symbols, the
first one is that it messes with your module's namespace. If the
symbol import above were in a file called "foo.py", the <code>foo</code> module
would then have the member <code>foo.IntegrityError</code>. Additionally, it
makes the code more difficult to understand when you flatten the module's
namespace out; 500 lines down in the file if you see <code>acct_m = AccountManager()</code>
as a developer new to the file you'll have to go up to the top and figure
out where the hell <code>AccountManager</code> is actually coming from to understand
how it works.</p>
<p>As code with these sort of symbol-level imports ages, it becomes more and more
frustrating to deal with, if I need <code>OperationalError</code> in my module now I have
three options:</p>
<ul>
<li>Update the line to say: <code>from MySQLdb import IntegrityError, OperationalError</code></li>
<li>Add <code>import MySQLdb</code> and just refer to <code>IntegrityError</code> and <code>MySQLdb.OperationalError</code></li>
<li>Add <code>import MySQLdb</code> and update all references to <code>IntegrityError</code></li>
</ul>
<p>I've seen code in open source projects that have abused the symbol imports
so badly that an import statement look like: <code>from mod import CONST1, CONST2, CONST3, SomeError, AnotherClass</code>
(ad infinium).</p>
<p>I think poor import style is a good indicator of how one can expect the
rest of the Python code to look, I cannot recall a single instance where I've
looked at a Python module with gross import statements and clean classes and functions.</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">from</span> MySQLdb <span style="color: #ff7700;font-weight:bold;">import</span> IntegrityError, OperationalError, MySQLError, ProgrammingError, \</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> NotSupportedError, InternalError</div></li></ol></pre></div>
<p>PYRAGE!
<!--break--></p>
http://unethicalblogger.com/posts/2009/12/pyrage_toolbox_import_hammer#commentsOpinionPythonSoftware DevelopmentThu, 24 Dec 2009 08:23:26 +0000R. Tyler Croy246 at http://unethicalblogger.comOne year of Cheetah
http://unethicalblogger.com/posts/2009/12/one_year_cheetah
<p>While working at <a id="aptureLink_yEeNgnHrmv" href="http://twitter.com/slideinc">Slide</a> I had a tendency to self-assign major projects,
not content with things being "good-enough" I tended to push and over-extend
myself to improve the state of Slide Engineering. Sometimes these projects
would fail and I would get uncomfortably close to burning myself out, other times,
such as the migration from <a id="aptureLink_RiTUKpPp5v" href="http://www.unethicalblogger.com/posts/2008/11/delightfully_wrong_about_git">Subversion to Git</a>, turned out to be incredibly rewarding
and netted noticable improvements in our workflow as a company.</p>
<p>One of my very first major projects was upgrading our installation of <a id="aptureLink_uxR2vwVN22" href="http://en.wikipedia.org/wiki/CheetahTemplate">Cheetah</a> from 1.0 to
2.0, at the time I vigorously <em>hated</em> Cheetah. My distain of the templating system stemmed
from using a three year old version (that sucked to begin with) and our usage of Cheetah which
bordered between "hackish" and "vomitable." At this point in Slide's history, the growth of
the Facebook applications meant there was going to be far less focus on the Slide.com codebase
which is where some of the more egregious Cheetah code lived; worth noting that I never "officially"
worked on the Slide.com codebase. When I successfully convinced <a id="aptureLink_hqxRXAFs0S" href="http://twitter.com/jerobi">Jeremiah</a> and <a id="aptureLink_MaZ97GDvZ4" href="http://www.linkedin.com/pub/ken-brownfield/2/b0/b49">KB</a> that it was worth
my time and some of their time to upgrade to Cheetah 2.0 which offered a number of improvements
that we could make use of, I still held some pretty vigorous hatred towards Cheetah. My attitude was
simple though, temporary pain on my part would alleviate pain inflicted on the rest of the engineering
team further down the line. Thanks to fantastic QA by Ruben and Sunil, the Cheetah upgrade went down
relatively issue free, things were looking fine in production and everybody went back to their
regularly scheduled work.</p>
<p>Months went by without me thinking of Cheetah too much until late 2008, Slide continued to write
front-end code using Cheetah and developers continued to grumble about it. Frustrated by the
lack of development on the project, I did the unthinkable, I started fixing it. Over the Christmas
break, I used <a id="aptureLink_rIMU5Wn8T7" href="http://www.kernel.org/pub/software/scm/git/docs/git-cvsimport.html">git-cvsimport(1)</a> to create a git repository from the Cheetah CVS repo hosted with
<a id="aptureLink_mPIIeTpoJW" href="http://www.crunchbase.com/company/sourceforge">SourceForge</a> and I started applying patches that had circulated on the mailing list. By mid-March
I had a number of changes and improvements in my fork of Cheetah and I released "Community
Cheetah". Without project administrator privileges on SourceForge, I didn't have much of a choice
but to publish a fork on <a id="aptureLink_1h1STzYjMV" href="http://www.crunchbase.com/company/github">GitHub</a>. Eventually I was able to get a hold of <a id="aptureLink_295JgMxNNc" href="http://www.linkedin.com/pub/tavis-rudd/3/207/817">Tavis Rudd</a>, the original
author of Cheetah who had no problem allowing me to become the maintainer of Cheetah proper,
in a matter of months I had gone from hating Cheetah to fulfilling the oft touted saying "it's
open source, fix it!" What was I thinking.</p>
<p>Thanks in part to git and GitHub's collaborative/distributed development model patches started to
come in and the Cheetah community for all intents and purposes "woke up." Over the course of the
past year, Cheetah has seen an amazing number of improvements, bugfixes and releases. Cheetah now
properly supports unicode throughout the system, supports @staticmethod and @classmethod decorators,
supports use with Django and now supports Windows as a "first-class citizen". While I committed
the majority of the fixes to Cheetah, five other developers contributed fixes:</p>
<ul>
<li><a id="aptureLink_M6cwowbGDF" href="http://www.linkedin.com/in/jbquenot">Jean-Baptiste Quenot</a> (unicode fixes)</li>
<li><a id="aptureLink_wmWMUg3S3M" href="http://fedoraproject.org/wiki/MikeBonnet">Mike Bonnet</a> (unicode fixes, test fixes)</li>
<li><a id="aptureLink_rENnnWb3Pw" href="http://www.linkedin.com/pub/james-abbatiello/2/589/421">James Abbatiello</a> (Windows support)</li>
<li><a id="aptureLink_sQvNrSWDj6" href="http://github.com/arunk">Arun Kumar</a></li>
<li>Doug Knight (fixes for #raw directive)</li>
</ul>
<p>In 2008, Cheetah saw 7 commits and 0 releases, while 2009 brought 342 commits and 10 releases;
something I'm particularly proud of. Unforunately since I've left Slide, I no longer use Cheetah
in a professional context but I still find it tremendously useful for some of my personal projects.</p>
<p>I am looking forward to what 2010 will bring for the Cheetah project, which started in mid-2001 and has
seen continued development since thanks to a number of contributors over the years.</p>
http://unethicalblogger.com/posts/2009/12/one_year_cheetah#commentsCheetahPythonSoftware DevelopmentSun, 20 Dec 2009 01:04:49 +0000R. Tyler Croy245 at http://unethicalblogger.comPyrage: Generic Exceptions
http://unethicalblogger.com/posts/2009/12/pyrage_generic_exceptions
<p>Earlier while talking to <a id="aptureLink_obXTzaiLXt" href="http://bitbucket.org/which_linden/">Ryan</a> I decided I'd try to coin the term "<a id="aptureLink_kDiulq8xAO" href="http://search.twitter.com/search?q=pyrage">pyrage</a>" referring to some frustrations I was having with some Python packages. The notion of "pyrage" can extend to anything from a constant irritation to a pure "WTF were you thinking!" kind of moment.</p>
<p>Not one to pass up a good opportunity to bitch publicly, I'll elaborate on some of my favorite sources of "pyrage", starting with generic exceptions. While at <a id="aptureLink_KugjVYAv84" href="http://www.crunchbase.com/company/slide">Slide</a>, one of the better practices I picked up from <a id="aptureLink_rgxVh01Btf" href="http://twitter.com/stuffonfire">Dave</a> was the use of specifically typed exceptions to specific errors. In effect:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">class</span> Connection<span style="color: black;">(</span><span style="color: #008000;">object</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">## Pretend this object has "stuff"</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">pass</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">class</span> InvalidConnectionError<span style="color: black;">(</span><span style="color: #008000;">Exception</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">pass</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">class</span> ConnectionConfigurationError<span style="color: black;">(</span><span style="color: #008000;">Exception</span><span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">pass</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> </div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">def</span> configureConnection<span style="color: black;">(</span>conn<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">isinstance</span><span style="color: black;">(</span>conn, Connection<span style="color: black;">)</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">raise</span> InvalidConnectionError<span style="color: black;">(</span><span style="color: #483d8b;">'configureConnection requires a Connection object'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> conn.<span style="color: black;">connected</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">raise</span> ConnectionConfigurationError<span style="color: black;">(</span><span style="color: #483d8b;">'Connection (%s) is already connected'</span> <span style="color: #66cc66;">%</span> conn<span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">## etc </span></div></li></ol></pre></div>
<p>Django, for example, is pretty stacked with generic exceptions, using builtin exceptions like ValueError and AttributeError for a myriad of different kinds of exceptions. <a id="aptureLink_juWqt9ZOeK" href="http://docs.python.org/library/urllib2.html">urllib2's</a> HTTPError is good example as well, overloading a large number
of HTTP errors into one exception leaving a developer to catch them all, and check the code, a la:</p>
<div class="geshifilter"><pre class="geshifilter-python"><ol><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">try</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">urlopen</span><span style="color: black;">(</span><span style="color: #483d8b;">'http://some/url'</span><span style="color: black;">)</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">except</span> <span style="color: #dc143c;">urllib2</span>.<span style="color: black;">HTTPError</span>, e:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">if</span> e.<span style="color: #dc143c;">code</span> == <span style="color: #ff4500;">503</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #808080; font-style: italic;">## Handle 503's special</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">pass</span></div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">else</span>:</div></li><li style="font-family: monospace; font-weight: normal;"><div style="font-family: monospace; font-weight: normal; font-style: normal"> <span style="color: #ff7700;font-weight:bold;">raise</span></div></li></ol></pre></div>
<p>Argh. pyrage!
<!--break--></p>
http://unethicalblogger.com/posts/2009/12/pyrage_generic_exceptions#commentsOpinionPythonSoftware DevelopmentFri, 18 Dec 2009 06:10:13 +0000R. Tyler Croy244 at http://unethicalblogger.comPython/JSON Eat/Drink-up in San Francisco
http://unethicalblogger.com/posts/2009/12/pythonjson_eatdrinkup_san_francisco
<p>A few weeks ago when I started working more on <a id="aptureLink_hhyMRd3A8o" href="http://search.twitter.com/search?q=py-yajl">py-yajl</a> I discovered that there are actually a number of <a id="aptureLink_a5GTJ4H6TM" href="http://en.wikipedia.org/wiki/Python%20%28programming%20language%29">Python</a> developers who work(ed) with JSON parsers in the Bay Area. As luck would have it <a id="aptureLink_dBhA8T1qeq" href="http://twitter.com/lloydhilaiel">Lloyd</a>, the author of <a id="aptureLink_SbD3Rzx991" href="http://lloyd.github.com/yajl/">Yajl</a>, is going to be in town next weekend for <a id="aptureLink_WynkcrwDeo" href="http://twitter.com/AddonCon">Add-on-Con</a>, time to meet up and have some beers! I've invited the authors of: <a id="aptureLink_KNyBnc5k59" href="http://code.google.com/p/simplejson/">simplejson</a>, <a id="aptureLink_ZbikePoaB3" href="http://pypi.python.org/pypi/jsonlib">jsonlib</a>, <a id="aptureLink_iEdZR6OG34" href="http://pypi.python.org/pypi/jsonlib2/">jsonlib2</a> and a few other Python hackers in the Bay Area that I know of.</p>
<p>If you're in San Francisco this Saturday (Dec. 12th) and loves you some Python, don't hesitate to swing by <a id="aptureLink_TTUxypvCoS" href="http://twitter.com/21stAmendment">21st Amendment</a> around 1pm-ish to join us!</p>
http://unethicalblogger.com/posts/2009/12/pythonjson_eatdrinkup_san_francisco#commentsMiscellaneousPythonWed, 09 Dec 2009 08:36:31 +0000R. Tyler Croy242 at http://unethicalblogger.comServer-side image transforms in Python
http://unethicalblogger.com/posts/2009/12/serverside_image_transforms_python
<p>While working at <a id="aptureLink_LQdA2xFWcb" href="http://twitter.com/slideinc">Slide</a>, I became enamored with the concept of cooperative threads (coroutines) and the in-house library built around <a id="aptureLink_uF9ePt8EiT" href="http://pypi.python.org/pypi/greenlet">greenlet</a> to implement coroutines for Python. As an engineer on the "server team" I had the joy of working in a coro-environment on a daily basis but now that I'm "out" I've had to find an alternative library to give me coroutines: <a id="aptureLink_k3TaZzEP9q" href="http://eventlet.net/doc/">eventlet</a>. Interestingly enough, eventlet shares common ancestry with Slide's internal coroutine implementation like two different species separated thousands of years ago by continental drift (a story for another day).</p>
<p>A few weekends ago, I had a coroutine itch to scratch one afternoon: an eventlet-based image server for applying transforms/filters/etc. After playing around for a couple hours "<a id="aptureLink_MaMftEzfE4" href="http://github.com/rtyler/PILServ/commits/master">PILServ</a>" started to come together. One of the key features I wanted to have in my little image server project was the ability to not only pass the server a URL of an image instead of a local path but also to "chain" transforms in a jQuery-esque style. Using segments of the URL as arguments, a user can arbitrarily chain arguments into PILServ, i.e.:</p>
<pre><code>http://localhost:8080/flip/filter(blur)/rotate(45)/resize(64x64)/<url to an image>
</code></pre>
<p>At the end of the evening I spent on PILServ, I had something going that likely shows off more of the skills of <a id="aptureLink_my0NPtWw65" href="http://www.pythonware.com/products/pil/">PIL</a> rather than eventlet itself but I still think it's <em>neat</em>. Below is a sample of some images transformed by PILServ running locally:</p>
<p><center><a href="http://agentdero.cachefly.net/scratch/pilserv.png" rel='lightbox'><img src="http://agentdero.cachefly.net/scratch/pilserv.png" width="450" border="0"/></a></center></p>
http://unethicalblogger.com/posts/2009/12/serverside_image_transforms_python#commentsMiscellaneousPythonSoftware DevelopmentSat, 05 Dec 2009 06:51:33 +0000R. Tyler Croy240 at http://unethicalblogger.comOn GitHub and how I came to write the fastest Python JSON module in town
http://unethicalblogger.com/posts/2009/12/github_and_how_i_came_write_fastest_python_json_module_town
<p>Perhaps the title is a bit too much ego stroking, yes, I did write the fastest Python module for decoding JSON strings and encoding Python objects to JSON. I didn't however write the parser behind the scenes.</p>
<p>Over the summer I discovered "<a id="aptureLink_n24z7kSMi1" href="http://lloyd.github.com/yajl/">Yet Another JSON Library</a>" on <a id="aptureLink_u0eQz9GMNI" href="http://www.crunchbase.com/company/github">GitHub</a>, written by <a id="aptureLink_YqaYOvz7FP" href="http://twitter.com/lloydhilaiel">Lloyd Hilaiel</a>, jonesing for a Saturday afternoon project I started the "<a id="aptureLink_iih8O9gONv" href="http://search.twitter.com/search?q=py-yajl">py-yajl</a>" project to see if I could implement a Python C module atop Lloyd's marvelous parsing library. After tinkering with the project for a while I got a working prototype building (learning how to define custom types in Python along the way) and let the project stagnate as my weekend ended and the workweek resumed.</p>
<p>A little over a week ago "<a id="aptureLink_S2nwrzEgQp" href="http://github.com/autodata">autodata</a>", another GitHub user, sent me a "Pull Request" with some minor changes to make py-yajl build cleaner on amd64; my interest in the project was suddenly reignited, amazing what a little interest can do for motivation. Over the 10 days following autodata's pull request I discovered that a former colleague of mine and fellow GitHub user "<a id="aptureLink_mY3NgqZfrq" href="http://twitter.com/teepark">teepark</a>" had forked the project as well, working on Python 3 support. Going from zero to <strong>two</strong> people interested in the project, I quickly converted the code from a stagnant, borderline embarrassing, dump of C code into a leak-free, swift JSON library
for Python. Not one to miss out on the fun, I pinged Lloyd who quickly became as enamored with making py-yajl the best Python JSON module available, he forked the project and almost immediately sent a number of pull requests my way with further optimizations to py-yajl such as:</p>
<ul>
<li>Swapping out the use of Python lists to a custom pointer stack for maintaining internal state</li>
<li>Accelerating parsing and handling of Number objects</li>
<li>Pruning a few memory leaks here and there</li>
</ul>
<p>Thanks to <a id="aptureLink_CZHm3Z4vyV" href="http://twitter.com/mikeal">mikeal</a>'s <a id="aptureLink_2E75jRgjq1" href="http://www.mikealrogers.com/archives/695">JSON post</a> and <a href="http://gist.github.com/239887">jsonperf.py</a> script, Lloyd and I could both see how py-yajl was stacking up against <a id="aptureLink_kofLpe0ikl" href="http://pypi.python.org/pypi/python-cjson">cjson</a>, jsonlib, <a id="aptureLink_V0T79aEWbu" href="http://code.google.com/p/jsonlib2/">jsonlib2</a> and <a id="aptureLink_bZhlC8WgRE" href="http://code.google.com/p/simplejson/">simplejson</a>; things got competitive. Below are the most recent <code>jsonperf.py</code> results with py-yajl v0.1.1:</p>
<pre><code>json.loads: 6470.22037ms
simplejson.loads: 202.21063ms
yajl.loads: 145.32621ms
cjson.decode: 102.44788ms
json.dumps: 2309.15286ms
cjson.encode: 276.49586ms
simplejson.dumps: 201.59785ms
yajl.dumps: 161.00153ms
</code></pre>
<p>Over the coming days or weeks (as time permits) I'm planning on adding JSON stream parsing support, i.e. parsing a stream of data as it's coming in off a socket or file object, as well as a few other miscellaneous tasks.</p>
<p>Given the nature of GitHub's social coding dynamic, py-yajl got off the ground as a project but Yajl itself gained an IRC channel (#yajl on Freenode) and a mailing list ([email protected]). To date I have over 20 unique repositories on GitHub (i.e. authored by me) but the experience around Yajl has been the most exciting and finally proved the "social coding" concept beneficial to me.</p>
http://unethicalblogger.com/posts/2009/12/github_and_how_i_came_write_fastest_python_json_module_town#commentsGitPythonSoftware DevelopmentFri, 04 Dec 2009 09:30:09 +0000R. Tyler Croy239 at http://unethicalblogger.com