<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>late.am</title><link href="https://late.am/" rel="alternate"></link><link href="https://late.am/feed.xml" rel="self"></link><id>https://late.am/</id><updated>2018-06-21T09:00:00-04:00</updated><entry><title>Flask-PyMongo, Back from the Dead</title><link href="https://late.am/post/2018/06/21/flask-pymongo-back-from-the-dead.html" rel="alternate"></link><published>2018-06-21T09:00:00-04:00</published><updated>2018-06-21T09:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2018-06-21:/post/2018/06/21/flask-pymongo-back-from-the-dead.html</id><summary type="html">&lt;p&gt;&lt;img alt="Sprouting seeds" src="/static/sprouting-seeds.jpg"&gt;
&lt;em&gt;&lt;a href="https://homesteading.com/"&gt;homesteading.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Long ago, when I worked at &lt;a href="https://www.mongodb.com/"&gt;MongoDB&lt;/a&gt; I created
&lt;a href="http://flask-pymongo.readthedocs.io/en/latest/"&gt;Flask-PyMongo&lt;/a&gt; to make it
easy for programmers using &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; to use the
database. Fast forward almost 8 years, during which time I wasn't a
consistent user of either Flask or MongoDB, and Flask-PyMongo has fallen
into disrepair.&lt;/p&gt;
&lt;p&gt;MongoDB, PyMongo, and Flask have moved on, and Flask-PyMongo hasn't been
kept up to date. There are more than twice as many forks as pull requests, a
GitHub &lt;a href="http://knowyourmeme.com/memes/the-ratio"&gt;ratio&lt;/a&gt; I'm not proud of.
Fotunately, the future for Flask-PyMongo is bright.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img alt="Sprouting seeds" src="/static/sprouting-seeds.jpg"&gt;
&lt;em&gt;&lt;a href="https://homesteading.com/"&gt;homesteading.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Long ago, when I worked at &lt;a href="https://www.mongodb.com/"&gt;MongoDB&lt;/a&gt; I created
&lt;a href="http://flask-pymongo.readthedocs.io/en/latest/"&gt;Flask-PyMongo&lt;/a&gt; to make it
easy for programmers using &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; to use the
database. Fast forward almost 8 years, during which time I wasn't a
consistent user of either Flask or MongoDB, and Flask-PyMongo has fallen
into disrepair.&lt;/p&gt;
&lt;p&gt;MongoDB, PyMongo, and Flask have moved on, and Flask-PyMongo hasn't been
kept up to date. There are more than twice as many forks as pull requests, a
GitHub &lt;a href="http://knowyourmeme.com/memes/the-ratio"&gt;ratio&lt;/a&gt; I'm not proud of.
Fotunately, the future for Flask-PyMongo is bright.&lt;/p&gt;


&lt;h1&gt;False Starts and New Beginnings&lt;/h1&gt;
&lt;p&gt;At PyCon US 2017, I first had the idea to restore Flask-PyMongo. PyCon
always has this effect on me, but sadly the effect is often short lived. In
2017, I got as far as &lt;a href="https://github.com/dcrosta/flask-pymongo/commit/74ebb61ec257318b2737a4086519bb231b259e77"&gt;mentioning plans for a 2.0
release&lt;/a&gt;,
but did not go into any detail, nor begin to make any progress toward that
goal.&lt;/p&gt;
&lt;p&gt;This year at PyCon 2018, I once again had the urge to work on Flask-PyMongo.
I had the same conversation with &lt;a href="https://emptysqua.re/blog/"&gt;Jesse&lt;/a&gt;,
decided, again, to jump from 0.x to 2.0, and even came up with the same
technical plan as the previous year (about which more below). All without
realizing that I had been down this road once before.&lt;/p&gt;
&lt;p&gt;I can now confidently say that Flask-PyMongo 2.0 is (soon to be) a real
thing, and it will set the stage for easier maintainability into the future
and a better experience for users and contributors. Flask-PyMongo 2.0 will
be released in early July, and pre-release versions are &lt;a href="https://pypi.org/project/Flask-PyMongo/#history"&gt;available
today&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;What's Changing&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Flask-PyMongo 2.0 is not backwards compatible!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A lot of the historical problems with Flask-PyMongo have cenetered on the
confusing and difficult configuration system. Originally, I envisioned that
users would want configuration abstracted from PyMongo itself, and created a
system where you could set Flask configurations for &lt;code&gt;MONGO_HOST&lt;/code&gt;,
&lt;code&gt;MONGO_PORT&lt;/code&gt;, and &lt;code&gt;MONGO_DBNAME&lt;/code&gt;, and be off to the races. For a while this
worked, and many users seemed to like it. Unfortunately, there are quite a
lot of configuration options for PyMongo, so the list of configurations
&lt;a href="https://flask-pymongo.readthedocs.io/en/0.5.2/#configuration"&gt;grew&lt;/a&gt;. Worse,
PyMongo and MongoDB are under active development, and grow and lose features
over time. Attempts to make Flask-PyMongo version-agnostic added tremendous
complexity to the configuration system, and evidently frustrated many users
over the years.&lt;/p&gt;
&lt;p&gt;In any event, it turns out that there's a better way to configure PyMongo --
with &lt;a href="https://docs.mongodb.com/manual/reference/connection-string/"&gt;MongoDB
URIs&lt;/a&gt;. Most
hosted PyMongo services already provide configuration information in exactly
this format. Going forward in 2.0, MongoDB URIs are the preferred
configuration method for Flask-PyMongo. Flask-PyMongo will only look for or
respect a single Flask configuration variable, &lt;code&gt;MONGO_URI&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you prefer, you may also pass positional and keyword arguments directly
to Flask-PyMongo, which will be passed through to the underlying PyMongo
&lt;code&gt;MongoClient&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Flask-PyMongo no longer supports configurating multiple instances via Flask
configuration. If you wish to use multiple Flask-PyMongo instances, you must
configure at least some of them using a URI or direct argument passing.&lt;/p&gt;
&lt;p&gt;Flask-PyMongo 2.0 also clarifies the support policy for versions of Flask,
PyMongo, MongoDB, and Python that are supported. For Flask and PyMongo, it
supports "recent" versions -- those versions with releases in the preceding
3 years (give or take). For MongoDB, we follow the &lt;a href="https://www.mongodb.com/support-policy"&gt;MongoDB server support
policy&lt;/a&gt;, and support versions that
are not end-of-lifed. For Python, we support 2.7 for as long as it is
supported by the CPython core maintainers; and the most recent 3 versions of
the 3.x series. For an exact list of supported versions and combinations,
see &lt;a href="https://github.com/dcrosta/flask-pymongo/blob/master/tox.ini"&gt;the build
matrix&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;What You Should Do&lt;/h1&gt;
&lt;p&gt;If you are a Flask-PyMongo user and you are using the 0.x series, you should
immediately pin a particular version. Flask-PyMongo 2.0 is not backwards
compatible, so you should take steps to ensure that you don't accidentally
break your application.&lt;/p&gt;
&lt;p&gt;If you are already using a URI for Flask-PyMongo configuration, or if that
is an easy change for you, I would appreciate if you could upgrade, test
compatibility, and report any issues &lt;a href="https://github.com/dcrosta/flask-pymongo/issues"&gt;on
GitHub&lt;/a&gt;. You can install
Flask-PyMongo 2.0 pre-releases with &lt;code&gt;pip install --pre flask-pymongo&lt;/code&gt;. You
may also want to follow the general discussion and release notices in
&lt;a href="https://github.com/dcrosta/flask-pymongo/issues/110"&gt;issue #110&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I also hope for Flask-PyMongo to be a place that supports Flask and MongoDB
with more than just connection assistance. Please suggest ideas and propose
contributions!&lt;/p&gt;</content><category term="python"></category><category term="flask"></category><category term="mongodb"></category></entry><entry><title>Now Seeking PyGotham Program Committee Members</title><link href="https://late.am/post/2018/05/13/seeking-pygotham-program-committee-members.html" rel="alternate"></link><published>2018-05-13T12:11:00-04:00</published><updated>2018-05-13T12:11:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2018-05-13:/post/2018/05/13/seeking-pygotham-program-committee-members.html</id><summary type="html">&lt;p&gt;&lt;img alt="Conference badges" src="/static/badges.jpg"&gt;
&lt;em&gt;&lt;a href="https://www.flickr.com/photos/marcthiele/"&gt;marc thiele&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Last year’s PyGotham had a great program committee of four organizers and
eight volunteers. Unfortunately due to the constrained timeline of the 2017
conference and everyone’s individual schedules, we had only a few chances to
meet, and never as the full group. The major consequence of this is that the
final talk decisions were effectively made by a smaller group, mostly
conference staff. We failed, by way of logistics, in our mission to make
PyGotham as community focused as possible. This year we aim to do better.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img alt="Conference badges" src="/static/badges.jpg"&gt;
&lt;em&gt;&lt;a href="https://www.flickr.com/photos/marcthiele/"&gt;marc thiele&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Last year’s PyGotham had a great program committee of four organizers and
eight volunteers. Unfortunately due to the constrained timeline of the 2017
conference and everyone’s individual schedules, we had only a few chances to
meet, and never as the full group. The major consequence of this is that the
final talk decisions were effectively made by a smaller group, mostly
conference staff. We failed, by way of logistics, in our mission to make
PyGotham as community focused as possible. This year we aim to do better.&lt;/p&gt;


&lt;h2&gt;Changes to the Program Process&lt;/h2&gt;
&lt;p&gt;As a guiding principal for programming the conference, we want the
organizers to act more like coaches than players in selecting the talks. We
retain the right to change the lineup, but we want most of the calls to be
made by the volunteer committee members.&lt;/p&gt;
&lt;p&gt;To that end, this year we’re going to experiment with a different process
than in previous years. We will form sub-committees from among the
volunteers, and these subcommittees will each program a mini conference of
talks about a particular topic area. The final topic breakdown will be
guided by the set of talks we get before the close of our CFP later this
month, but we expect committees on Data Science &amp;amp; Machine Learning, Web
Programming, Testing, and so on.&lt;/p&gt;
&lt;p&gt;Each subcommittee will be given a sub-set of talks from the total list as a
suggested starting point. These talks will be initially ordered by the rank
order of &lt;a href="/post/2017/07/20/pygotham-voting-open.html"&gt;public votes&lt;/a&gt; (2018
talk voting will open a few days after the CFP closes), but picking the top
N won’t necessarily make the best conference.  Each subcommittee should feel
free to nominate talks from other topic areas’ lists if there’s a strong
reason to (or if we have made a mistake in categorization).&lt;/p&gt;
&lt;p&gt;The job of the subcommittee will be to apply a human touch and to arrange
the talks, to the extent possible, into “mini-tracks” of 2 or 3 talks. We
will in all likelihood pick a few more talks for each subcommittee than can
actually fit into the final conference — we expect there to be some cases of
speakers being selected more than once, and we will need some speakers to be
on a wait-list in case anyone drops out between now and October. Still, it
is my hope that we can arrange these mini tracks into the final PyGotham
schedule more or less as-is.&lt;/p&gt;
&lt;p&gt;All of the review by the subcommittees will be anonymous, so after all the
committees have produced their mini-tracks, the organizers will de-anonymize
the talk list to ensure that we haven’t selected multiple talks by a single
speaker.&lt;/p&gt;
&lt;h2&gt;Getting Involved&lt;/h2&gt;
&lt;p&gt;Here’s where we need help from you: so far, the program committee doesn’t
exist! If you think you’ll have a few hours to spare over the first half of
June, and you want to help shape PyGotham, then we want to hear from you.
Drop us a line at &lt;a href="mailto:program@pygotham.org"&gt;program@pygotham.org&lt;/a&gt;, and
let us know if there’s a particular topic area you’re interested in helping
to review. Thanks in advance for your help! &lt;/p&gt;</content><category term="python"></category><category term="conferences"></category><category term="pygotham"></category></entry><entry><title>Diversity at PyGotham</title><link href="https://late.am/post/2018/04/27/diversity-at-pygotham-2018.html" rel="alternate"></link><published>2018-04-27T10:45:00-04:00</published><updated>2018-04-27T10:57:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2018-04-27:/post/2018/04/27/diversity-at-pygotham-2018.html</id><summary type="html">&lt;p&gt;This year, for the first time, PyGotham is asking everyone who has submitted
a talk to our &lt;a href="https://www.papercall.io/pygotham-2018"&gt;Call for Proposals&lt;/a&gt;
to fill out a brief demographic survey to help support our efforts to ensure
that PyGotham is representative of the Python community we believe in,
welcoming to all our conference attendees, and helps to broaden and advance
the conversation on diversity and representation that the wider software
development community is undertaking.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Multi-colored crayons" src="/static/crayons.jpg"&gt;
&lt;em&gt;&lt;a href="https://www.flickr.com/photos/saaleha/"&gt;Saaleha Bamjee&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This year, for the first time, PyGotham is asking everyone who has submitted
a talk to our &lt;a href="https://www.papercall.io/pygotham-2018"&gt;Call for Proposals&lt;/a&gt;
to fill out a brief demographic survey to help support our efforts to ensure
that PyGotham is representative of the Python community we believe in,
welcoming to all our conference attendees, and helps to broaden and advance
the conversation on diversity and representation that the wider software
development community is undertaking.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Multi-colored crayons" src="/static/crayons.jpg"&gt;
&lt;em&gt;&lt;a href="https://www.flickr.com/photos/saaleha/"&gt;Saaleha Bamjee&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;


&lt;h1&gt;Why is Diversity Important to Us?&lt;/h1&gt;
&lt;p&gt;Without any outside influence, experience has shown us that tech conference
speakers tend to predominantly come from a single background: male career
programmers. There are exceptions, but this is the pattern we see most often
at PyGotham and other tech conferences.&lt;/p&gt;
&lt;p&gt;At PyGotham we want to do more than reflect the current demographics of
professional developers. We want to hear from scientists and designers, from
teachers and students, from community organizers and political activists,
from everyone. Anyone with an interest in technology, be it academic,
recreational, or professional, has something interesting to give a talk
about. Often, we are too close to our work to notice what is unique,
misunderstood, or confusing about it, but with a little effort, great topics
can be found everywhere.&lt;/p&gt;
&lt;p&gt;In order to tap into that variety of topics beyond those of interest just to
professional programmers, we have to engage with speakers outside of that
group. This is, in a word, “diversity.”&lt;/p&gt;
&lt;h1&gt;The Diversity Survey&lt;/h1&gt;
&lt;p&gt;The organizers debated taking this step back and forth. Might collecting
this information frighten away speakers from under-represented groups,
exactly those we most want to attract? Would it spark controversy and enrage
majority members who nevertheless take every opportunity to cast themselves
as victims? Would anyone even bother to fill it out?&lt;/p&gt;
&lt;p&gt;The argument that won the day, ultimately, is that in order to improve the
diversity of speakers at PyGotham, we first need to know something about
what kind of diversity we have. Last year, I eyeballed the list of speakers
and &lt;a href="/2017/09/25/pygotham-talk-voting-retrospective.html"&gt;guesstimated that about 23% of talks had a female
speaker&lt;/a&gt; (some talks
had multiple speakers, so we were probably a bit lower as a share of
speakers in general). This is comparable to women’s representation numbers
at a variety of large tech companies, but as they are all pursuing internal
improvements to their diversity and inclusion programs, we too want to aim
higher.&lt;/p&gt;
&lt;p&gt;Of course, I don’t mean to simplify all of “diversity” or “inclusion” to
just the share of women speakers. It is the only measure on which we have
any data at all, even as unreliable as name-game guessing. It’s from this
position that we decided to launch the survey this year.&lt;/p&gt;
&lt;h1&gt;What Information We Request&lt;/h1&gt;
&lt;p&gt;We will be asking about gender identity, ethnicity, level of speaking experience,
amount of programming experience, and age.&lt;/p&gt;
&lt;h1&gt;How We Will Use the Results&lt;/h1&gt;
&lt;p&gt;PyGotham’s program selection process this year will be similar to last
year’s: we will begin with a round of public, anonymous voting beginning
shortly after the CFP ends in late May. During public voting in 2017, 116
people cast over 12,000 votes for the nearly 200 proposals we had. (I hope
we can count on the community to show a similar amount of engagement this
year!)&lt;/p&gt;
&lt;p&gt;The public talk voting is anonymous. Demographic information about the
author of each proposal will not be available to public voters nor PyGotham
organizers during this time. To do so would risk introducing bias, and the
purpose of public voting is to take the pulse of likely attendees with
regards to their level of interest in the submissions we receive.&lt;/p&gt;
&lt;p&gt;After public voting, a smaller committee of PyGotham organizers and
volunteers from the community will work together to select the 60 talks that
will make it into the final conference. The vast majority of this process is
also anonymous, and will be conducted without regard to the demographic
survey’s results.&lt;/p&gt;
&lt;p&gt;After we have a very strong candidate talk list, we must de-anonymize the
results to ensure that a) no individual speaker has too many talk slots
(this is unfair both to that speaker and to others who, by definition, don’t
get those slots), and b) to ensure that we have the proper balance of
speaker backgrounds. We will use the survey results to ensure that among the
major axes, and for the information that we have collected, that our final
speaker lineup represents at least as much diversity as is present in our
overall submission pool.&lt;/p&gt;
&lt;p&gt;The often frustrating reality of putting together a conference schedule is
that the committee is often forced to choose a small number of talks out of
a pool of excellent proposals. Voting helps us break the tie in one
dimension — how can we produce a conference that the attendees will enjoy
and learn from — and the demographic survey results will help us break ties
in another dimension, to favor having a more diverse rather than less
diverse speaker lineup.&lt;/p&gt;
&lt;h1&gt;Further up the Pipeline&lt;/h1&gt;
&lt;p&gt;Even if every submitter fills out the survey, by the time we are making
individual talk decisions, it is often too late to do much to move the
needle on diversity. We can ensure that our speaker lineup is representative
of the submitter pool, but what if even that pool doesn’t live up to our
expectations for what PyGotham could be?&lt;/p&gt;
&lt;p&gt;Building on the success of last year’s &lt;a href="https://www.meetup.com/nycpython/events/240192272/"&gt;Proposal
Brainstorming&lt;/a&gt; workshop,
we are holding at least 4 similar events this year, with one more event
scheduled on &lt;a href="https://www.meetup.com/NYC-PyLadies/events/249924249/"&gt;May 2 with NYC
PyLadies&lt;/a&gt;. At all of
these events, attendees get a chance to work with one another to suss out
what is great and talk-worthy about each of their experience.&lt;/p&gt;
&lt;p&gt;We are also planning to proactively reach out to a number of NYC-area tech meetups
and societies representing women, people of color, and other
under-represented groups, in order to solicit their members to participate
in our call for proposals.&lt;/p&gt;</content><category term="python"></category><category term="conferences"></category><category term="pygotham"></category></entry><entry><title>PyGotham Talk Voting Retrospective</title><link href="https://late.am/post/2017/09/25/pygotham-talk-voting-retrospective.html" rel="alternate"></link><published>2017-09-25T12:20:00-04:00</published><updated>2017-09-25T12:32:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2017-09-25:/post/2017/09/25/pygotham-talk-voting-retrospective.html</id><summary type="html">&lt;p&gt;&lt;a href="https://2017.pygotham.org/"&gt;PyGotham 2017&lt;/a&gt; is less than two weeks away!
(Do you have your ticket yet? &lt;a href="https://pygotham2017.eventbrite.com/?discount=IVOTED"&gt;Use code "IVOTED" for 25%
off!&lt;/a&gt;) As we ramp up
for the final conference, I wanted to take a moment to share some thoughts
on our experiment with public talk voting this year.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Agile retrospective with lego figures" src="/static/lego-retrospective.jpg"&gt;
&lt;em&gt;&lt;a href="https://hakanforss.wordpress.com/"&gt;Håkan Forss&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="https://2017.pygotham.org/"&gt;PyGotham 2017&lt;/a&gt; is less than two weeks away!
(Do you have your ticket yet? &lt;a href="https://pygotham2017.eventbrite.com/?discount=IVOTED"&gt;Use code "IVOTED" for 25%
off!&lt;/a&gt;) As we ramp up
for the final conference, I wanted to take a moment to share some thoughts
on our experiment with public talk voting this year.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Agile retrospective with lego figures" src="/static/lego-retrospective.jpg"&gt;
&lt;em&gt;&lt;a href="https://hakanforss.wordpress.com/"&gt;Håkan Forss&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;


&lt;h1&gt;First, Some Facts and Stats&lt;/h1&gt;
&lt;p&gt;In some ways, our job on the program committee was very easy: about 150
people submitted close to 200 different talks. Having such a wide pool of
amazing talks ensured we could make PyGotham a conference that we're all
looking forward to. But the abundance of great talks forced us to make
painful choices: saying "no" to most of these talks was the hardest thing
the committee had to do this year.&lt;/p&gt;
&lt;p&gt;I would also be remiss if I didn't thank the 116 people who collectively
cast over 12,000 votes during the &lt;a href="https://late.am/post/2017/07/20/pygotham-voting-open.html"&gt;public
voting&lt;/a&gt; round of our process
this year.&lt;/p&gt;
&lt;p&gt;After public voting, there was another round of closed voting. Using the
feedback from the public voting round, we created batches of talks about
similar topics, and chose finalists from among each of those batches. Seven
volunteers, along with several of the organizers, donated another ten or so
hours of their time for this round of voting.&lt;/p&gt;
&lt;p&gt;Many thanks to all of you who took time to submit a talk or vote to help us
shape the schedule this year! We couldn't have done it without you.&lt;/p&gt;
&lt;h1&gt;What Worked Well&lt;/h1&gt;
&lt;p&gt;Public voting was a huge success. I never would have imagined we would have
so many people cast so many votes. I expected we might get a weak signal of
interest from a handful of die-hards, but instead we got a very strong and
consistent ranking across all the talks, with each talk receiving about 62
votes -- each vote for a talk was positive, negative, or neutral.
We had a good sample size and a lot of confidence that the
vote results reflect the interests of our community. (I am not a
statistician, and these statements have not been independently evaluated for
mathematical rigor.)&lt;/p&gt;
&lt;p&gt;In fact, all of our requests for help were met with overwhelming responses.
We had to turn away volunteers from the batch voting round and
program committee membership, since we wanted to keep the group small enough
to work well together in meetings -- we try to follow the &lt;a href="http://www.businessinsider.com/jeff-bezos-two-pizza-rule-for-productive-meetings-2013-10"&gt;two pizza
rule&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;What Could Have Been Better&lt;/h1&gt;
&lt;p&gt;First, we were -- or at least I felt like we were -- behind schedule
throughout much of the talk selection process. Prospective speakers who have
submitted to our CFP have an expectation about when we will get back to them
with final decisions, and we didn't meet that schedule. In part, this is
because the voting process takes time -- with 195 proposals to read and
consider, and in order to gather feedback from as wide an audience as
possible, we have to allow both voting rounds to go on for a fairly long
time. We should have been more transparent about the schedule, and we should
have designed a schedule with announcements to speakers much earlier than
this year.&lt;/p&gt;
&lt;p&gt;The batch voting round didn't provide as clear a signal about which talks to
accept into the final schedule as we had hoped. With so many excellent
proposals, and so few slots to fill, consensus was elusive. Some of the
organizers and I had to scramble at the last minute to make the process
efficient and productive, and we ended up creating a straw man proposal for
the final list, then soliciting feedback on this specific proposal from the
committee members. This was very efficient, but I fear it may not have
solicited as much input as another process.&lt;/p&gt;
&lt;p&gt;Finally, on diversity: out of 60 talk slots, 14 of them, 23 percent, have a
woman speaker, according to the very scientific "names that sound to Dan
like they are women" methodology (and caveat all the obvious flaws inherent
in this kind of counting). 23 percent is a bit higher than last year's
PyGotham, but it's not as high as we'd like. I won't attempt here to
quantify our speaker lineup according to other axes of diversity, other than
to say that I'm sure we have room to improve there, as well.&lt;/p&gt;
&lt;p&gt;Two main factors contributed to us falling short of our own goals: we failed
to reach out early enough to some groups that specifically work with underrepresented
groups, and we didn't assemble a list and individually contact
speakers from underrepresented groups to ask them to submit to our CFP. My
co-chair, &lt;a href="https://emptysqua.re/blog/"&gt;A. Jesse Jiryu Davis&lt;/a&gt;, and I are both
first-time organizers, and we definitely underestimated the amount of work
and time that simply putting a conference together would take. This is an
area he and I both care about, and aim to make major improvements in next
year.&lt;/p&gt;
&lt;p&gt;I would also like to have had a better gauge and guide for diversity than
the "does this name sound like an X" method. I hope next year to be able to
include at least one demographic question on our CFP, "are you
a member of an underrepresented group in technology?", which we
can use to better assess the results of our diversity and outreach efforts.&lt;/p&gt;
&lt;h1&gt;See You In A Few Weeks&lt;/h1&gt;
&lt;p&gt;One of the most rewarding aspects of this process, which can be demanding at
times, is all of the people I've gotten to talk to, email with, and meet as
an organizer of PyGotham. Though it sometimes felt like it was against all
odds, we have a full schedule of amazing talks and speakers, and I'm looking
forward to putting faces with a lot of these names in under two weeks. See
you at the conference!&lt;/p&gt;</content><category term="python"></category><category term="conferences"></category><category term="pygotham"></category></entry><entry><title>PyGotham Talk Voting is Open!</title><link href="https://late.am/post/2017/07/20/pygotham-voting-open.html" rel="alternate"></link><published>2017-07-20T10:00:00-04:00</published><updated>2017-07-20T10:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2017-07-20:/post/2017/07/20/pygotham-voting-open.html</id><summary type="html">&lt;p&gt;&lt;img alt="&amp;quot;I Voted&amp;quot; stickers" src="/static/i-voted.jpg"&gt;&lt;/p&gt;
&lt;p&gt;For the first time, the PyGotham program committee is looking for you, our
potential attendees, speakers, and community, to help us shape the
conference by voting on the 195 talk proposals we've received. We're going
to hold open voting until August 7th, after which the Program Committee will
use the votes to inform our final selections for the conference.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img alt="&amp;quot;I Voted&amp;quot; stickers" src="/static/i-voted.jpg"&gt;&lt;/p&gt;
&lt;p&gt;For the first time, the PyGotham program committee is looking for you, our
potential attendees, speakers, and community, to help us shape the
conference by voting on the 195 talk proposals we've received. We're going
to hold open voting until August 7th, after which the Program Committee will
use the votes to inform our final selections for the conference.&lt;/p&gt;


&lt;h1&gt;How You Can Help&lt;/h1&gt;
&lt;p&gt;We want PyGotham to reflect the interests and desires of our community, so
we ask that you share your time to help review the talk submissions. We're
asking a single, simple question for each talk: would you like to see this
talk in the final PyGotham schedule? You can give each talk either a +1 ("I
would definitely like to see this talk"), a 0 ("I have no preference on this
talk"), or a -1 ("I do not think this talk should be in PyGotham").&lt;/p&gt;
&lt;p&gt;You can sign up for an account and begin voting at
&lt;a href="https://vote.pygotham.org/user/new/"&gt;vote.pygotham.org&lt;/a&gt;. The talk review
web site will present you with talks in random order, omitting the ones you
have already voted on. For each talk, you will see this form:&lt;/p&gt;
&lt;p&gt;&lt;img alt="+1/0/-1 voting form" src="/static/pygotham-voting-form.png"&gt;&lt;/p&gt;
&lt;p&gt;Be sure to click "Save Vote" to make sure your vote is recorded. Once you
do, a button will appear to jump to the next proposal.&lt;/p&gt;
&lt;p&gt;Many thanks to &lt;a href="http://www.njl.us/"&gt;Ned Jackson Lovely&lt;/a&gt; for sharing
&lt;a href="https://github.com/njl/progcom"&gt;progcom&lt;/a&gt;, the US PyCon talk voting app,
which we are using (with light adaptation) for PyGotham. Thanks Ned!&lt;/p&gt;</content><category term="python"></category><category term="conferences"></category><category term="pygotham"></category></entry><entry><title>Submit to PyGotham's CFP</title><link href="https://late.am/post/2017/06/07/submit-to-pygotham.html" rel="alternate"></link><published>2017-06-07T09:02:00-04:00</published><updated>2017-06-07T09:02:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2017-06-07:/post/2017/06/07/submit-to-pygotham.html</id><summary type="html">&lt;p&gt;I'm honored to be an organizer on the program committee for &lt;a href="https://2017.pygotham.org/"&gt;PyGotham
2017&lt;/a&gt; this year, and I encourage you all to
&lt;a href="https://www.papercall.io/pygotham-2017"&gt;submit a talk&lt;/a&gt; to our little
conference. PyGotham will be held October 6, 7, and 8 in New York City, and
the Call for Proposals is open until July 18.&lt;/p&gt;
&lt;p&gt;PyGotham attendees are diverse, come from varied backgrounds and skill
levels, and have lives and interests beyond Python programming. Accordingly,
the topics at PyGotham often vary a bit more widely than other programming
language conferences. In the past, we have hosted talks about subjects from
&lt;a href="http://pyvideo.org/pygotham-2015/using-python-for-sarcasm-detection-in-speech.html"&gt;detecting sarcasm in audio files of
speech&lt;/a&gt;,
to &lt;a href="https://www.youtube.com/watch?v=CRXiNMS9JuY"&gt;open source stenography&lt;/a&gt;;
from &lt;a href="http://pyvideo.org/pygotham-2016/making-games.html"&gt;game programming&lt;/a&gt;
to &lt;a href="http://pyvideo.org/pygotham-2014/how-to-shut-down-tolkien.html"&gt;what we can learn about code review from J.R.R.
Tolkien&lt;/a&gt;
(sort of).&lt;/p&gt;
&lt;p&gt;The threads connecting all these talks together are Python and New York, and
the people interested and involved in both of those. If you're a member of
either of these communities, if you think the PyGotham audience would like
to hear your talk, then we want to see your proposal&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I'm honored to be an organizer on the program committee for &lt;a href="https://2017.pygotham.org/"&gt;PyGotham
2017&lt;/a&gt; this year, and I encourage you all to
&lt;a href="https://www.papercall.io/pygotham-2017"&gt;submit a talk&lt;/a&gt; to our little
conference. PyGotham will be held October 6, 7, and 8 in New York City, and
the Call for Proposals is open until July 18.&lt;/p&gt;
&lt;p&gt;PyGotham attendees are diverse, come from varied backgrounds and skill
levels, and have lives and interests beyond Python programming. Accordingly,
the topics at PyGotham often vary a bit more widely than other programming
language conferences. In the past, we have hosted talks about subjects from
&lt;a href="http://pyvideo.org/pygotham-2015/using-python-for-sarcasm-detection-in-speech.html"&gt;detecting sarcasm in audio files of
speech&lt;/a&gt;,
to &lt;a href="https://www.youtube.com/watch?v=CRXiNMS9JuY"&gt;open source stenography&lt;/a&gt;;
from &lt;a href="http://pyvideo.org/pygotham-2016/making-games.html"&gt;game programming&lt;/a&gt;
to &lt;a href="http://pyvideo.org/pygotham-2014/how-to-shut-down-tolkien.html"&gt;what we can learn about code review from J.R.R.
Tolkien&lt;/a&gt;
(sort of).&lt;/p&gt;
&lt;p&gt;The threads connecting all these talks together are Python and New York, and
the people interested and involved in both of those. If you're a member of
either of these communities, if you think the PyGotham audience would like
to hear your talk, then we want to see your proposal&lt;/p&gt;


&lt;h1&gt;The Importance of Outlines&lt;/h1&gt;
&lt;p&gt;PyGotham's CFP, like many other Python conferences, explicitly suggests you
draft an outline of your talk as part of the submission. This has obvious
benefits to those of us on the program committee -- we can get a clearer
vision of what your talk will actually be like -- so we ask for it. But you
may not be interested in why the outline is a benefit to us on the program
committee, so let me tell you why it is a boon to you:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The outline is where you really solidify what your talk is about. You may
formulate your talk beginning from a library you want to explore, a
programming trick you like, or a pithy title &amp;mdash; each of us has a
different creative process. But you won't really know if your idea can stand
on its own until you've fleshed out what you want to say about it. &lt;a href="http://rhodesmill.org/brandon/"&gt;Brandon
Rhodes&lt;/a&gt; summarizes this nicely:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By the time I finish a proposal, I feel about halfway done writing the
talk. That feeling is always hilariously inaccurate, but it hopefully
signals the point at which I have gotten enough detail into the proposal
for the committee to imagine what the talk will be like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(From &lt;a href="http://rhodesmill.org/brandon/2013/example-pycon-proposals/"&gt;Example PyCon talk
proposals&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Once you have an outline written, your abstract, longer description, and
title practically write themselves, so I recommend you start with the
outline first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The next major benefit is structure. Some talks suffer from a lack of
coherent organization, which makes it difficult for the audience to follow
along. At the stage of an outline, you won't be too attached to particular
phrasing, slide ordering, or example code, so you're more free to rearrange
topics within your talk to be most comprehensible.&lt;/p&gt;
&lt;p&gt;I like to structure my talks a bit like a five-paragraph essay, a
technique I learned in high school. First, I'm going to introduce a
problem, topic, or thesis; next, I will provide supporting evidence, and
develop it for most of the duration of the talk; and finally, I return to
the original problem statement or thesis and form a conclusion or suggest
next steps.&lt;/p&gt;
&lt;p&gt;I want my audience to have the experience that I have watching a
documentary, rather than reading reference documentation. You should learn
something, and you should be engaged throughout; but you shouldn't have to
keep notes or refer back to previous pages (or slides). The moment when
your ideas are complete enough to write down, but not so set in your
brain that you're attached is the right time to organize this structure;
that time is when you can write your outline.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, your outline also answers an important question: do I have
enough here to talk about for 25 minutes? Do I have more than enough, and
should I request a 40 minute slot? Timing talks is tricky, especially if you
haven't spoken much before, but it's hopeless to try to guess from a vague
idea whether that can sustain a proper talk slot. You'll feel better about
your proposal knowing how long each section will take, and using that
framework to guide writing your actual talk once you're selected.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A lot of credit for the above belongs to &lt;a href="https://about.me/celiala"&gt;Celia
La&lt;/a&gt;, Sarah Guido, &lt;a href="http://www.jasonamyers.com/"&gt;Jason
Myers&lt;/a&gt;, and &lt;a href="https://www.jonafato.com/"&gt;Jon
Banafato&lt;/a&gt;, who joined me on a &lt;a href="https://www.meetup.com/nycpython/events/240192272/"&gt;panel at the NYC
Python meetup&lt;/a&gt; last week
to discuss the PyGotham CFP and conference talk submissions in general.&lt;/p&gt;
&lt;h1&gt;Resources for the CFP&lt;/h1&gt;
&lt;p&gt;A few specific resources were mentioned during the panel, as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://pyvideo.org/pycon-us-2016/a-jesse-jiryu-davis-write-an-excellent-programming-blog-pycon-2016.html"&gt;Write An Excellent Programming
  Blog&lt;/a&gt;
  by &lt;a href="https://emptysqua.re/blog/"&gt;A. Jesse Jiryu Davis&lt;/a&gt; describes five common
  blog post archetypes. Not all are appropriate for a conference talk, but
  understanding these can help shape your idea indevelopment. Jesse
  is my co-chair on the PyGotham program committee.&lt;/li&gt;
&lt;li&gt;Jesse also has &lt;a href="https://emptysqua.re/blog/seven-tips-for-pycon/"&gt;an article about getting talks into
  PyCon&lt;/a&gt; with a lot of
  great advice.&lt;/li&gt;
&lt;li&gt;Finally, although I have not yet read it all (it's long!), &lt;a href="https://hynek.me/"&gt;Hyneck
  Schlawack&lt;/a&gt; recently published a piece on &lt;a href="https://hynek.me/articles/speaking/"&gt;how he
  prepares and gives conference talks&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Ask Me Anything&lt;/h1&gt;
&lt;p&gt;I've promised in several tweets to be available if you have any questions
about PyGotham or the CFP, or if you want help brainstorming an idea. The
best way to get in touch with me is on
&lt;a href="https://twitter.com/lazlofruvous"&gt;Twitter&lt;/a&gt;, so drop me a line.&lt;/p&gt;
&lt;p&gt;PyGotham's Call for Proposals is open until July 18, so don't delay!&lt;/p&gt;</content><category term="python"></category><category term="conferences"></category><category term="pygotham"></category></entry><entry><title>Introducing Tox-Docker</title><link href="https://late.am/post/2017/05/21/introducing-tox-docker.html" rel="alternate"></link><published>2017-05-21T10:05:00-04:00</published><updated>2017-05-30T08:04:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2017-05-21:/post/2017/05/21/introducing-tox-docker.html</id><summary type="html">&lt;p&gt;Today I released &lt;a href="https://github.com/dcrosta/Tox-Docker/"&gt;Tox-Docker&lt;/a&gt; to
GitHub and the &lt;a href="https://pypi.python.org/pypi/tox-docker/"&gt;Python Package
Index&lt;/a&gt;. Tox-Docker is a plugin for
&lt;a href="https://tox.readthedocs.io/"&gt;Tox&lt;/a&gt;, which, you guessed it, manages one or
more &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; containers during test runs.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Today I released &lt;a href="https://github.com/dcrosta/Tox-Docker/"&gt;Tox-Docker&lt;/a&gt; to
GitHub and the &lt;a href="https://pypi.python.org/pypi/tox-docker/"&gt;Python Package
Index&lt;/a&gt;. Tox-Docker is a plugin for
&lt;a href="https://tox.readthedocs.io/"&gt;Tox&lt;/a&gt;, which, you guessed it, manages one or
more &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt; containers during test runs.&lt;/p&gt;


&lt;h1&gt;Why Tox-Docker?&lt;/h1&gt;
&lt;p&gt;Tox-Docker began its life because I needed to test some code that uses the
&lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;
&lt;a href="https://www.postgresql.org/docs/9.4/static/datatype-json.html"&gt;JSONB&lt;/a&gt;
column type. In another life, I might have done this by instructing
&lt;a href="https://jenkins.io/"&gt;Jenkins&lt;/a&gt; to first install Postgres, start the server,
create a user, create a database, and so on. There's even a small chance
that this would work well, some of the time -- so long as tests didn't fail,
the build didn't die unexpectedly without cleaning up, multiple tests didn't
run at once, and so on. In fact, I've done exactly this sort of hackery a
few times in the past already. It is dirty, and often requires manual
cleanup after failures.&lt;/p&gt;
&lt;p&gt;So, when confronted with the need to write tests that talk to a real
Postgres instance, rather than reaching into my old toolbox I was determined
to find a better solution. Docker can run multiple instances of Postgres at
the same time in isolation from one another, which obviates the need for
mutual exclusion of builds. Docker containers are lightweight to start, and
easy to clean up (you can delete them all at once with a single command), so
when the tests are done, we can simply remove it and move on.&lt;/p&gt;
&lt;p&gt;There was still the question of how to manage the lifecycle of the
container, though, which is where Tox comes in. Tox is a test automation
tool that standardizes an interface between your machine (or your continuous
integration environment) and your test code. Like Docker, Tox encourages
isolation, by creating a clean &lt;a href="https://virtualenv.pypa.io/"&gt;virtualenv&lt;/a&gt; for
each test run, free of old package installs, custom hacks, and so on. Tox
already has a well-defined set of steps it runs to build your package,
install dependencies, start tests, and gather results. Happily, it allows
plugins to hook into this sequence to add custom behavior.&lt;/p&gt;
&lt;h1&gt;How Tox-Docker Works&lt;/h1&gt;
&lt;p&gt;Tox's plugins implement callback hooks to participate in the test workflow.
For Tox-Docker, we use the pre-test and post-test hooks, which set up and
tear down our Docker environment, respectively. Importantly, the post-test
hook runs regardless of whether the tests passed, failed, or errored,
ensuring that we'll have an opportunity to clean up any Docker containers we
started during the pre-test hook. Finally, Tox plugins can also hook into
the configuration system, so that projects using Tox-Docker can specify what
Docker containers they require.&lt;/p&gt;
&lt;p&gt;The simplest use of Tox-Docker is to specify the Docker image or images,
including version tags, that are required during test runs. For instance, if
your project requires Postgres, you might add this to your &lt;code&gt;tox.ini&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[testenv]&lt;/span&gt;
&lt;span class="na"&gt;docker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;With Tox-Docker installed, the next time you run &lt;code&gt;tox&lt;/code&gt;, you will see
something like the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;py27&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pull&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;postgres:9.6&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;py27&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;postgres:9.6&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;py27&lt;/span&gt; &lt;span class="n"&gt;runtests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PYTHONHASHSEED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;944551639&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;py27&lt;/span&gt; &lt;span class="n"&gt;runtests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="n"&gt;test_your_project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;
&lt;span class="o"&gt;=============================&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="k"&gt;session&lt;/span&gt; &lt;span class="n"&gt;starts&lt;/span&gt; &lt;span class="o"&gt;==============================&lt;/span&gt;
&lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="n"&gt;linux2&lt;/span&gt; &lt;span class="c1"&gt;-- Python 2.7.12, pytest-3.0.7, py-1.4.33, pluggy-0.4.0&lt;/span&gt;
&lt;span class="n"&gt;rootdir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;travis&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inifile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="n"&gt;collected&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;
&lt;span class="n"&gt;test_your_project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;===========================&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;===========================&lt;/span&gt;
&lt;span class="n"&gt;py27&lt;/span&gt; &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;remove&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;72e2ffea02&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forced&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Tox-Docker has picked up your configuration, and pulled and started a
PostgreSQL container, which it shuts down after the tests finish. This is
equivalent to running &lt;code&gt;docker pull&lt;/code&gt;, &lt;code&gt;docker run&lt;/code&gt;, and &lt;code&gt;docker rm&lt;/code&gt;
yourself, but without the manual hassle.&lt;/p&gt;
&lt;h1&gt;Challenges and Helpers&lt;/h1&gt;
&lt;p&gt;Not every Dockerized component can be started and be expected to "just
work". Most services will require or allow for some amount of configuration,
and your tests will need some information back out of Docker to know how to
use the services. In particular, we need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A way to pass settings into Docker containers, in cases where the
defaults are not sufficient&lt;/li&gt;
&lt;li&gt;A way to inform the tests how to communicate with the service inside the
container, specifically, what ports are exposed&lt;/li&gt;
&lt;li&gt;A way to delay beginning the test run until the container has started and
the application within it is ready to work&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tox-Docker lets you specify environment variables with the &lt;code&gt;dockerenv&lt;/code&gt;
setting in the &lt;code&gt;tox.ini&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;[testenv]&lt;/span&gt;
&lt;span class="na"&gt;docker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6&lt;/span&gt;
&lt;span class="na"&gt;dockerenv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&lt;/span&gt;
&lt;span class="s"&gt;    POSTGRES_USER=user_name&lt;/span&gt;
&lt;span class="s"&gt;    POSTGRES_DB=database_name&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Tox-Docker takes these variables and passes them to Docker as it launches
the container, just as you might do manually with the &lt;code&gt;--env&lt;/code&gt; flag. These
variables are also made available in the environment that tests run in, so
they can be used to construct a connection string, for instance.&lt;/p&gt;
&lt;p&gt;Additionally, Tox-Docker interrogates the container just after it's started,
to get the list of exposed TCP or UDP ports. For each port, Tox-Docker
constructs an environment variable named after the container and exposed
port, whose value is the host-side port number that Docker has mapped the
exposed port to. Postgres listens on TCP port 5432 within the container,
which might be mapped to port 32187 on your host system. In this case, an
environment variable &lt;code&gt;POSTGRES_5432_TCP&lt;/code&gt; will be set with value "32187".&lt;/p&gt;
&lt;p&gt;Tests can use these environment variables to parameterize connections to the
Dockerized services, rather than having to hard-code knowledge of the
environment.&lt;/p&gt;
&lt;p&gt;Finally, in order to avoid false-negative test failures or errors,
Tox-Docker waits until it can connect to each of the ports exposed by the
Docker container. This is not a perfect way to determine that the service
inside is actually ready, but Docker provides no way for a service inside
the container to signal to the outside world that it's finished starting up.
In practice, I hope that this heuristic is good enough.&lt;/p&gt;
&lt;h1&gt;Future Work&lt;/h1&gt;
&lt;p&gt;The most obvious next step for Tox-Docker is to support &lt;a href="https://docs.docker.com/compose/"&gt;Docker
Compose&lt;/a&gt;, the Docker tool that lets you
launch a cluster of interconnected containers in a single command. For the
projects I am working with, I haven't yet had need of Docker Compose, but
for projects of a certain level of complexity this will be preferable to
attempting to manually manage this in &lt;code&gt;tox.ini&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Installation and Feedback&lt;/h1&gt;
&lt;p&gt;Tox-Docker is available in the &lt;a href="https://pypi.python.org/pypi/tox-docker/"&gt;Python Package
Index&lt;/a&gt; for installation via &lt;code&gt;pip
install tox-docker&lt;/code&gt;. Contributions, suggestions, questions, and feedback are
welcome via &lt;a href="https://github.com/dcrosta/Tox-Docker/"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;</content><category term="python"></category><category term="testing"></category><category term="docker"></category><category term="tox"></category></entry><entry><title>Delete Your Dead Code!</title><link href="https://late.am/post/2016/04/28/delete-your-dead-code.html" rel="alternate"></link><published>2016-04-28T00:00:00-04:00</published><updated>2016-05-02T07:55:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2016-04-28:/post/2016/04/28/delete-your-dead-code.html</id><summary type="html">&lt;p&gt;A few days ago, &lt;a href="http://nedbatchelder.com/"&gt;Ned Batchelder&lt;/a&gt;'s post on
&lt;a href="http://nedbatchelder.com/text/deleting-code.html"&gt;deleting code&lt;/a&gt; made the
rounds on &lt;a href="https://news.ycombinator.com/item?id=11541474"&gt;HN&lt;/a&gt;, even though
it was originally written in 2002. Here I want to echo a few of Ned's
points, and take a stronger stance than he did: delete code as soon as you
know you don't need it any more, no questions asked. I'll also offer some
tips from the trenches for how to identify candidate dead code.&lt;/p&gt;
&lt;p&gt;This is the second in an ongoing series on &lt;a href="/tag/eat-your-vegetables.html"&gt;eating your
vegetables&lt;/a&gt; in software engineering, on good,
healthy practices for a happy and successful codebase. I don't (yet) know
how long the series will be, so please stay tuned!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A few days ago, &lt;a href="http://nedbatchelder.com/"&gt;Ned Batchelder&lt;/a&gt;'s post on
&lt;a href="http://nedbatchelder.com/text/deleting-code.html"&gt;deleting code&lt;/a&gt; made the
rounds on &lt;a href="https://news.ycombinator.com/item?id=11541474"&gt;HN&lt;/a&gt;, even though
it was originally written in 2002. Here I want to echo a few of Ned's
points, and take a stronger stance than he did: delete code as soon as you
know you don't need it any more, no questions asked. I'll also offer some
tips from the trenches for how to identify candidate dead code.&lt;/p&gt;
&lt;p&gt;This is the second in an ongoing series on &lt;a href="/tag/eat-your-vegetables.html"&gt;eating your
vegetables&lt;/a&gt; in software engineering, on good,
healthy practices for a happy and successful codebase. I don't (yet) know
how long the series will be, so please stay tuned!&lt;/p&gt;


&lt;h1&gt;What Is Dead May Never Die&lt;/h1&gt;
&lt;p&gt;This heading isn't just an oh-so-clever and timely pop culture reference.
Dead code, that is, code that can't possibly be executed by your program, is
a real hindrance to the maintainability of your codebase. How many times have
you gone to add what seemed like a simple feature or improvement, only to be
stymied by the complexity of the code you have to work around and within?
How much nicer would your life be if the practice of adding a feature or
fixing a bug was as easy as you actually thought it would be during sprint
planning?&lt;/p&gt;
&lt;p&gt;Each time you want to make a change, you must consider how it interacts with
each of the existing features, quirks, known bugs, and limitations of all
the code that surrounds it. By having less code surrounding the feature you
want to add, there's less to consider and less that can go wrong. Dead code
is especially pernicious, because it looks like you need to consider
interactions with it, but, since it's dead, it's merely a distraction. It
can't possibly benefit you since it's never called.&lt;/p&gt;
&lt;p&gt;The fact that dead code might never actually die is an existential threat to
your ability to work with a given codebase. In the limit, if code that isn't
called is never culled, the size of your application will grow forever.
Before you know it, what might only be a few thousand lines of actual
functionality is surrounded by orders of magnitude more code that, by
definition, does nothing of value.&lt;/p&gt;
&lt;h1&gt;It's Got to Go&lt;/h1&gt;
&lt;p&gt;Ned (Batchelder, not
&lt;a href="http://gameofthrones.wikia.com/wiki/Eddard_Stark"&gt;Stark&lt;/a&gt;) was a little more
nuanced and diplomatic than I'm going to be here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let's say you have a great class, and it has many methods. One day you
discover that you no longer are calling a particular method. Do you leave
it in or take it out?&lt;/p&gt;
&lt;p&gt;There's no single answer to the question, because it depends on the class
and the method. The answer depends on whether you think the method might be
called again in the future.&lt;/p&gt;
&lt;p&gt;From &lt;a href="http://nedbatchelder.com/text/deleting-code.html"&gt;http://nedbatchelder.com/text/deleting-code.html&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I say: scorch the earth and leave no code alive. The best code is code that
you don't even have.&lt;/p&gt;
&lt;p&gt;For those less audacious than I, remember that version control has your back in
case you ever need that code again.&lt;/p&gt;
&lt;p&gt;That said, I've never experienced a need to add something back that I have
previously deleted, at least not in the literal sense of adding back in,
line for line, verbatim, a section of code I'd previously deleted.&lt;/p&gt;
&lt;p&gt;Of course I'm not talking about things like reverting wrong-headed commits
here -- we're all human, and I make as many mistakes as the next person.
What I mean is, I've never deleted a feature, shipped to production, then
come back weeks, or months later and thought to myself, "boy howdy, that
code I wrote a year or more ago was probably pretty good, so let's put it
back now." Codebases live and evolve with time, so the old code probably
doesn't fit with the new ideas, techniques, frameworks, and styles in use
today. I might refer back to the old version for a refresher, especially if
it's particularly subtle, but I've never brought code back in, wholesale.&lt;/p&gt;
&lt;p&gt;So, do yourself -- and your team -- a favor, and delete dead code as soon as
you notice it.&lt;/p&gt;
&lt;h1&gt;How Did We Get Here?&lt;/h1&gt;
&lt;p&gt;&lt;a href="http://nedbatchelder.com/text/deleting-code.html"&gt;Ned's post&lt;/a&gt; goes into
great detail on how and why dead code happens -- perhaps the person making a
change didn't think the code would be gone forever, and so commented it out
or conditionally compiled it. Perhaps the person making a change didn't know
enough to know that the code was actually dead (about which more later).&lt;/p&gt;
&lt;p&gt;I'll add another hypothesis to the list: we might all just be lazy. It's
definitely easier not to do something (i.e. to leave the code as-is) than to
do something (delete it).&lt;/p&gt;
&lt;p&gt;Laziness is, after all, one of the &lt;a href="http://threevirtues.com/"&gt;three great virtues of a
programmer&lt;/a&gt;. But the Laziness that &lt;a href="http://www.wall.org/~larry/"&gt;Larry
Wall&lt;/a&gt; was talking about isn't this kind, but
another kind: "The quality that makes you go to great effort to reduce
overall energy expenditure." Viewed this way, deleting dead code is an act
of capital-L Laziness -- doing something that's easy now to prevent yourself
from having to do something hard later. We could all stand to develop more
of this kind of Laziness, what I like to think of as "disciplined laziness,"
in our day-to-day habits.&lt;/p&gt;
&lt;h1&gt;How Do We Get Out Of Here?&lt;/h1&gt;
&lt;p&gt;I spend most of my time programming in Python, where, unfortunately, IDEs
can't usually correctly analyze a complete codebase and identify
never-called code automatically. But, with a combination of discipline and
some run-time tooling, we can attack this problem from two sides.&lt;/p&gt;
&lt;p&gt;For simple cases, a better sense of situational awareness can help identify
and remove dead code while you're making changes. Imagine you're working on
a particular function, and you notice that a branch of an &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt; phrase
can't be executed based on the valid values of the surrounding code.  I call
this "dead code in the small," and this is quite easy to reason about and
remove, but it does require a bit more effort than one might ordinarily
expend.&lt;/p&gt;
&lt;p&gt;Until you develop the habit of noticing this during the course of your
ordinary programming routine, you can add a step to your pre-commit
checklist: review the code around your changes for any now-dead code. This
could happen just before you submit the code to your co-workers for review
(you do do code review, right?) so that they don't have to repeat that
process while reading through your changes.&lt;/p&gt;
&lt;p&gt;Another kind of dead code happens when you remove the last usage of a class
or function from within the code you're changing, without realizing that
it's the last place that uses it. This is "dead code in the large," and is
harder to discover in the course of ordinary programming unless you're
lucky enough to have &lt;a href="https://en.wikipedia.org/wiki/Eidetic_memory"&gt;eidetic
memory&lt;/a&gt; or know the codebase
like the back of your hand.&lt;/p&gt;
&lt;p&gt;This is where run-time tooling can help us out. At
&lt;a href="http://tech.magnetic.com/"&gt;Magnetic&lt;/a&gt;, we're using Ned's (yes, the same Ned)
&lt;a href="http://coverage.readthedocs.org/en/latest/"&gt;coverage.py&lt;/a&gt; package to help
inform our decisions about dead code. Ordinarily coverage is used during
testing to ensure that your test cases appropriately exercise the code under
test, but we also use it within our code "running as normal" to get insight
into what is or isn't used:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;coverage&lt;/span&gt;

&lt;span class="n"&gt;cov&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coverage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Coverage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;data_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/path/to/my_program.coverage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;auto_data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cover_pylib&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/path/to/my/program&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cov&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# ... do some stuff ...&lt;/span&gt;

&lt;span class="n"&gt;cov&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;cov&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This sets up a &lt;code&gt;Coverage&lt;/code&gt; object with a few options that make the report
more usable. First, we tell coverage where to save its data (we'll use that
later to produce a nice HTML report of what is and isn't used), and ask it
to automatically load and append to that file with &lt;code&gt;auto_data=True&lt;/code&gt;. Next we
ask it not to bother calculating coverage over the standard library or in
installed packages -- that's not our code, so we'd expect that a lot of
what's in there might not be used by us. It's not dead code that we need to
maintain, so we can safely ignore it. We ask it to compute branch coverage
(whether the true and false conditions of each &lt;code&gt;if&lt;/code&gt; statement are hit). And
finally, we point it out our sources, so that it can link its knowledge of
what is or isn't called back to the source code for report computation.&lt;/p&gt;
&lt;p&gt;After our program runs, we can compute the HTML coverage report like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;COVERAGE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/my_program.coverage coverage html -d /path/to/output
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Which generates a report like:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Sample HTML report from coverage.py" src="/static/coverage-report.png"&gt;&lt;/p&gt;
&lt;p&gt;(A &lt;a href="http://nedbatchelder.com/files/sample_coverage_html/index.html"&gt;complete example coverage HTML coverage
report&lt;/a&gt; is
available as part of the &lt;a href="http://coverage.readthedocs.org/en/latest/"&gt;coverage.py
docs&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;The lines highlighted in red are lines that were never hit during the
recorded execution of the program -- these are candidate lines (and, by
extension, methods) for dead code elimination.&lt;/p&gt;
&lt;p&gt;I'll leave you with three warnings about using this approach to finding and
removing dead code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Be careful when reviewing the results of a coverage run -- the fact that a
   line or function wasn't executed during &lt;em&gt;a single run&lt;/em&gt; of the program
   doesn't mean they're necessarily dead or unreachable in general. You must
   still check the codebase to determine whether they're completely dead in
   your application.&lt;/li&gt;
&lt;li&gt;Computing coverage means your program needs to do more work, so it will
   become slower when run in this mode. I wouldn't recommend running this all
   the time in production, but in a staging environment or in targeted
   scenarios you'll probably be OK. As always, if performance is an important
   concern, you should definitely measure what impact coverage has before you
   run it.&lt;/li&gt;
&lt;li&gt;Finally, dont trust code coverage reports from testing runs to find dead
   code. Some code might be dead, save for the tests that excercise it; and
   some code might be alive, but untested!&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Parting Thoughts&lt;/h1&gt;
&lt;p&gt;To you, dear reader, I must apologize. I left out an important part of Ned's
blog post when I quoted him earlier. He says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There's no single answer to the question, because it depends on the class
and the method. [...] A coarse answer could be: if the class is part of
the framework, then leave it, if it is part of the application, then
remove it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you're an author of a library or framework, rather than an application,
then the question of dead code becomes in some ways harder and in other ways
easier. In essence, you can't ever remove a part of your public API (except
during &lt;a href="http://semver.org/"&gt;major version bumps&lt;/a&gt;). Essentially, all of your
public API is live code, even if you, yourself, don't use it. But behind the
public interface, dead code can still happen, and it should still be removed.&lt;/p&gt;
&lt;p&gt;Delete your dead code!&lt;/p&gt;</content><category term="software development"></category><category term="eat your vegetables"></category><category term="python"></category></entry><entry><title>Demystifying Logistic Regression</title><link href="https://late.am/post/2016/04/22/demystifying-logistic-regression.html" rel="alternate"></link><published>2016-04-22T00:00:00-04:00</published><updated>2016-04-22T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2016-04-22:/post/2016/04/22/demystifying-logistic-regression.html</id><summary type="html">&lt;p&gt;For our hackathon this week, I, along with several co-workers, decided to
re-implement &lt;a href="https://github.com/JohnLangford/vowpal_wabbit"&gt;Vowpal Wabbit&lt;/a&gt;
(aka "VW") in &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt; as a chance to learn more about how
&lt;a href="https://en.wikipedia.org/wiki/Logistic_regression"&gt;logistic regression&lt;/a&gt;, a
common machine learning approach, works, and to gain some practical
programming experience with Go.&lt;/p&gt;
&lt;p&gt;Though our hackathon project focused on learning Go, in this post I want to
spotlight logistic regression, which is far simpler in practice than I had
previously thought. I'll use a very simple (perhaps simplistic?)
implementation in pure Python to explain how to train and use a logistic
regression model.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;For our hackathon this week, I, along with several co-workers, decided to
re-implement &lt;a href="https://github.com/JohnLangford/vowpal_wabbit"&gt;Vowpal Wabbit&lt;/a&gt;
(aka "VW") in &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt; as a chance to learn more about how
&lt;a href="https://en.wikipedia.org/wiki/Logistic_regression"&gt;logistic regression&lt;/a&gt;, a
common machine learning approach, works, and to gain some practical
programming experience with Go.&lt;/p&gt;
&lt;p&gt;Though our hackathon project focused on learning Go, in this post I want to
spotlight logistic regression, which is far simpler in practice than I had
previously thought. I'll use a very simple (perhaps simplistic?)
implementation in pure Python to explain how to train and use a logistic
regression model.&lt;/p&gt;


&lt;h1&gt;Predicting True or False&lt;/h1&gt;
&lt;p&gt;Logistic regression is a statistical learning technique useful for predicting
the probability of a binary outcome -- true or false. As we've &lt;a href="http://tech.magnetic.com/2015/06/click-prediction-with-vowpal-wabbit.html"&gt;previously
written&lt;/a&gt;, we use
logistic regression with VW to predict the likelihood of a user clicking on a
particular ad in a particular context, a true or false outcome.&lt;/p&gt;
&lt;p&gt;Logistic regression, like many machine learning systems, learns from a
"training set" of previous events, and tries to build predictions for new
events it hasn't seen before. For instance, we train our click prediction
model using several weeks of ad views (impressions) and ad clicks, and then
use that to make predictions about new events to determine how likely the user
is to click on an impression.&lt;/p&gt;
&lt;p&gt;Each data point in the training set is further broken down into "features,"
attributes of the event that we want the model to learn from. For online
advertising, common features include the size of the ad, the site on which the
ad is being shown, the location of the user viewing the ad, and so on.&lt;/p&gt;
&lt;p&gt;Logistic regression, or more accurately, &lt;a href="https://en.wikipedia.org/wiki/Stochastic_gradient_descent"&gt;Stochastic Gradient
Descent&lt;/a&gt;, the
algorithm that trains a logistic regression model, computes a weight to go
along with each feature. The prediction is the sum of the products of each
feature's value and each feature's weight, passed through the &lt;a href="https://en.wikipedia.org/wiki/Logistic_function"&gt;logistic
function&lt;/a&gt; to "squash" the
answer into a number in the range [0.0, 1.0].&lt;/p&gt;
&lt;p&gt;&lt;img alt="The standard logistic function" src="/static/logistic-function.png"&gt;
&lt;em&gt;The standard logistic function. Public Domain image from
&lt;a href="https://en.wikipedia.org/"&gt;Wikipedia&lt;/a&gt; by
&lt;a href="https://commons.wikimedia.org/wiki/User:Qef"&gt;Qef&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Applying a logistic regression model is therefore relatively easy, it's a
simple loop to write. What surprised me most during the hackathon project is
that training a logistic regression model, coming up with the values for the
weights we multiply by the features, is also surprisingly concise and
straightforward.&lt;/p&gt;
&lt;h1&gt;An Example&lt;/h1&gt;
&lt;p&gt;Let's consider a click data set. Each row in the table is an event we've
(hypothetically) recorded, either an impression which did not get a click, or
an impression plus click, along with a series of attributes about the event
that we'll use to build our regression model.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Site&lt;/th&gt;
&lt;th&gt;Browser&lt;/th&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;EventType&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;160x600&lt;/td&gt;
&lt;td&gt;wunderground.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;AZ&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;160x600&lt;/td&gt;
&lt;td&gt;ebay.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;IA&lt;/td&gt;
&lt;td&gt;click&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;msn.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;MS&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;160x600&lt;/td&gt;
&lt;td&gt;ebay.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;FL&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;popularscience.tv&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;CA&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;320x50&lt;/td&gt;
&lt;td&gt;weather.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;NB&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;latimes.com&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;CA&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;popularscience.tv&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;OR&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;msn.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;MI&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;dictionary.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;NY&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;ebay.com&lt;/td&gt;
&lt;td&gt;Safari&lt;/td&gt;
&lt;td&gt;OH&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;320x50&lt;/td&gt;
&lt;td&gt;msn.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;FL&lt;/td&gt;
&lt;td&gt;click&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;dictionary.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;NJ&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;urbanspoon.com&lt;/td&gt;
&lt;td&gt;Firefox&lt;/td&gt;
&lt;td&gt;OR&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;msn.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;FL&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;latimes.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;AZ&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;realtor.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;CA&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;deviantart.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;DC&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;weather.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;OH&lt;/td&gt;
&lt;td&gt;click&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;msn.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;AR&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;msn.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;MN&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;wunderground.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;MI&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;dictionary.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;OK&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;popularscience.tv&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;IN&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;dictionary.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;MA&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;weather.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;VT&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;320x50&lt;/td&gt;
&lt;td&gt;ebay.com&lt;/td&gt;
&lt;td&gt;Chrome&lt;/td&gt;
&lt;td&gt;OH&lt;/td&gt;
&lt;td&gt;click&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x600&lt;/td&gt;
&lt;td&gt;popularscience.tv&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;NJ&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;728x90&lt;/td&gt;
&lt;td&gt;weather.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;WA&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300x250&lt;/td&gt;
&lt;td&gt;weather.com&lt;/td&gt;
&lt;td&gt;IE&lt;/td&gt;
&lt;td&gt;MO&lt;/td&gt;
&lt;td&gt;impression&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These 30 data points will form our training data set. The model will learn
from these examples what impact the attributes -- size, site, browser, and US
state -- have on the likelihood of the user clicking on an ad that we show
them.&lt;/p&gt;
&lt;p&gt;In reality, to build a good predictive model you need many more events than
the 30 that I am showing here -- we use tens of millions in our production
modeling pipelines in order to get good performance and coverage of the
variety of values that we see in practice.&lt;/p&gt;
&lt;p&gt;To convert this data set to work with logistic regression, which computes a
binary outcome, we'll treat each click as a target value of 1.0, also known as
a "positive" event, and each impression as 0.0, or a "negative" event.&lt;/p&gt;
&lt;p&gt;We also need to account for the fact that each of our features is a string,
but logistic regression needs features that have values that it can multiply
by its weight to compute the sum-of-products. Our features don't have numeric
values, just some set of possibilities that each attribute can take on. For
instance, the size attribute is one of three ad sizes we work with, 300x250,
728x90, or 160x600.  Features that can take on some set of discrete values are
called "categorical" features.&lt;/p&gt;
&lt;p&gt;We could assign a numeric value to each of the possibilities, for instance use
the value 1 when the size is 300x250, 2 when the size is 728x90, and 3 when
the size is 160x600. This would work mechanically, but won't produce the
outcome that we want. Since the weight of the feature "size" is multiplied by
the value of the feature in each event, this would mean that we've decided,
arbitrarily, that 160x600 is three times more "clicky" than 300x250, which the
data set may not bear out. Moreover, for some attributes, like site, we have
many many possible values, and determining what numeric value to assign to
each of the possiblities is tedious, and will further amplify the problem as
described here.&lt;/p&gt;
&lt;p&gt;The proper solution to this problem is as effective as it is elegant: rather
than having 1 feature, "site" with values like "ebay.com" or "msn.com", we
create a much larger set of features, one for each site that we have seen in
our training or evaluation data set. For a given training data example, if the
"site" column has value "msn.com", we say that the feature "site:msn.com" has
value 1, and that all other features in the "site:..." family have value 0.
Essentially, we pivot the data by all the values of each categorical feature.
Of course, the total number of features in our training set becomes very
large, but, as we will see, SGD can accomodate this gracefully.&lt;/p&gt;
&lt;h1&gt;Stochastic Gradient Descent In Action&lt;/h1&gt;
&lt;p&gt;At the start of the hackathon, we wrote some very simple Python code to serve
as a reference for how logistic regression should work. Python reads almost
like pseudocode, so thats what we'll show here, too.&lt;/p&gt;
&lt;p&gt;We decided to model examples as Python dictionaries, with a top-level field
indicating the actual outcome (1.0 for clicks, 0.0 for impressions), and a
nested dictionary of features. Here's what one looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;actual&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;features&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;site&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ebay.com&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;size&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;160x600&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;browser&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;IE&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;state&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;IA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Given an example and a dictionary of &lt;code&gt;weights&lt;/code&gt;, we can predict the outcome
straighforwardly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;extract_features&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bias&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We initialize the score to 0, then for each feature in the example, we get the
weight from the dictionary. If the weight doesn't exist (because the model
hasn't been trained on any examples containing this feature), then we assume 0
as the weight, which leaves the score as-is.&lt;/p&gt;
&lt;p&gt;Additionally, we add in a "bias" weight, which you can think of as a feature
that every example has. The "bias" weight helps SGD correct for an overall
skew in our training data set one way or another. It's possible to leave this
out, but it's generally helpful to keep it.&lt;/p&gt;
&lt;p&gt;The expression at the end of this function is the logistic function, as we saw
above, which squashes the result into the range 0 to 1. We do this because we
are trying to predict a binary outcome, so we want our prediction to be
strictly between these two boundary values. If you do the math, you'll see
that for an empty model -- one with nothing in the &lt;code&gt;weights&lt;/code&gt; dictionary -- we
will predict 0.5 for any input example.&lt;/p&gt;
&lt;p&gt;Generating the features we use in SGD from our example is similarly
straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extract_features&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;features&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iteritems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Since our features are categorical, we can take advantage of Python's
dictionary to generate &lt;code&gt;(key, value)&lt;/code&gt; pairs as the actual features. Though all
our examples have values for each of the four attributes we're considering,
they each have different values -- thus the tuples generated by
&lt;code&gt;extract_features&lt;/code&gt; will be different for each example. This is our conversion
from attributes with a set of possible values, to features which carry the
implicit value 1.&lt;/p&gt;
&lt;p&gt;Finally, we have enough code in place to see how stochastic gradient descent
training actually works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;train_on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;prediction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;predict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;actual&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;gradient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prediction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;extract_features&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;current_weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_weight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;gradient&lt;/span&gt;

        &lt;span class="n"&gt;current_weight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bias&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bias&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_weight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;learning_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;gradient&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;For each example in the training set, we first compute the prediction, and,
along with the actual outcome, we compute the &lt;code&gt;gradient&lt;/code&gt;. The gradient is a
number between -1 and 1, and helps us adjust the weight for each feature, as
we'll see in a moment.&lt;/p&gt;
&lt;p&gt;Next, for each feature in the example, we update the weight for that feature
by adding or subtracting a small amount from the current weight, the
&lt;code&gt;learning_rate&lt;/code&gt;. Here we can now see how the gradient works in action.
Consider the case of a positive example (a click). The &lt;code&gt;actual&lt;/code&gt; value will be
1.0, so if our prediction is high (close to 1.0), we'll adjust the weights up
by a small amount; if the prediction is low, the gradient will be large, and
we'll adjust the weights up by a correspondingly large amount. On the other
hand, consider when the actual outcome is 0.0 for a negative event. If the
prediction is high, the gradient will be close to -1, so we'll lower the
weight by a relatively large amount; if the prediction is close to 0, we'll
lower the weight by a relatively small amount.&lt;/p&gt;
&lt;p&gt;For the first example, or more generally, the first time a given feature is
seen, the feature weight is 0. But, since each example has a different set of
features, over time the weights will diverge from one another, and converge on
values that tend to minimize the difference between the actual and predicted
value for each example.&lt;/p&gt;
&lt;h1&gt;Real-World Considerations&lt;/h1&gt;
&lt;p&gt;What I've shown here is a very basic implementation of logistic regression and
stochastic gradient descent. It is enough to begin making some predictions
from your data set, but probably not enough to use in a production system as
is. But I've taken up enough of your time with a relatively long post, so the
following features, then, are left as an excerise to the reader to implement:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;train_on&lt;/code&gt; function assumes that one pass through the training data is
   sufficient to learn all that we can or need to. In practice, often several
   passes are necessary. Different strategies can be used to determine when
   the model has "learned enough," but most rely on having a separate testing
   data set, composed of real examples the model will not train on. The error
   on these examples is critical to understanding how the model will perform
   in the wild.&lt;/li&gt;
&lt;li&gt;As it is written here, we only support categorical features. But what if
   the data set has real-valued features, like the historical CTR of the site or
   the user viewing the impression? To support this example, we need to multiply
   the weight by the feature value in &lt;code&gt;predict&lt;/code&gt;, and multiply the gradient by the
   feature value in &lt;code&gt;train_on&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This code doesn't perform any kind of
   &lt;a href="https://en.wikipedia.org/wiki/Regularization_(mathematics)"&gt;regularization&lt;/a&gt;,
   a technique used to help combat over-fitting of the model to the training
   data. Generally speaking, regularization nudges the weights of all features
   closer to 0 every iteration, which over time has the effect of reducing the
   importance to the final prediction of infrequently-seen features.&lt;/li&gt;
&lt;/ol&gt;</content><category term="data science"></category><category term="python"></category></entry><entry><title>Should Sense</title><link href="https://late.am/post/2015/11/05/should-sense.html" rel="alternate"></link><published>2015-11-05T00:00:00-05:00</published><updated>2015-11-05T00:00:00-05:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2015-11-05:/post/2015/11/05/should-sense.html</id><summary type="html">&lt;p&gt;I've just finished reading &lt;a href="https://medium.com/engineering-leadership/software-engineer-traits-fbf5dee9289c"&gt;Dan Pupius' Medium article on Software Engineer
Traits&lt;/a&gt;,
where he lays out four characteristics of great software engineers. I agree
with his assessment, and want to add one more quality to the discussion --
something I call "should sense".&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I've just finished reading &lt;a href="https://medium.com/engineering-leadership/software-engineer-traits-fbf5dee9289c"&gt;Dan Pupius' Medium article on Software Engineer
Traits&lt;/a&gt;,
where he lays out four characteristics of great software engineers. I agree
with his assessment, and want to add one more quality to the discussion --
something I call "should sense".&lt;/p&gt;


&lt;p&gt;In a nutshell, "should sense" is that thing which drives a great developer
to dig deeper, to not be satisfied with the answers she or he has at hand.
It manifests as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Should&lt;/em&gt; it take this long to read ten gigabytes of data off disk?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Should&lt;/em&gt; we have to reach past the public API of this library to
  get it to do what we want?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Should&lt;/em&gt; it require fifty lines of hairy code to solve this problem?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Should&lt;/em&gt; this test be failing one in a hundred times?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I don't mean things like "should I add another branch to this long if/else
block," or "should I extract this method to share it with the other place
that's using it." Those are all questions that good developers ought to be
able to answer, and that inexperienced ones can learn how to answer.
Refactoring is both a skill and a sense of judgement in its own right
(possibly one that deserves its own post). But that's not the kind of
"should" I'm talking about here.&lt;/p&gt;
&lt;p&gt;What I mean is a gut feel for when something is wrong with a &lt;em&gt;working&lt;/em&gt;
program. Should sense is associated with, and maybe drives, good habits like
questioning assumptions, performance testing, diving deeper, and developing
expertise with frameworks and tools. Or perhaps it's that engineers who have
those good habits have or are on the road to building their should sense.&lt;/p&gt;
&lt;p&gt;It's possible that Dan meant some of these things when he enumerated his
four traits. In particular, it seems likely that some portion of "Curiosity"
and "Awareness" are touching on some of the ideas I'm mentioning here, even
if he doesn't spell it out explicitly.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://emptysqua.re/blog/"&gt;Jesse Davis&lt;/a&gt;, who was kind enough to review an
early draft of this post, pointed out that should sense is a lot like what
&lt;a href="http://www.thisamericanlife.org/contributors/ira-glass"&gt;Ira Glass'&lt;/a&gt;
describes in his essay &lt;a href="https://vimeo.com/85040589"&gt;Taste&lt;/a&gt;. He desbribes how
artists and creative workers start out with great taste, a sense of what
they want to create, but that it takes a lot of hard-won experience to grow
their skills to the point where their own work lives up to their taste, to
what they know it could be.&lt;/p&gt;
&lt;p&gt;In technology, I think the opposite happens: it's fairly easy to get started
with programming (especially today in the age of a seemingly infinite number
of online tutorials, coding schools, and MOOCs), and at first, the world
seems your oyster, as you can make the machines do ever more impressive
things. What I think we don't learn until we've fought the hard battles,
probably many times, is the technical equivalent of good taste: knowing what
your code &lt;em&gt;should&lt;/em&gt; look like, and how it &lt;em&gt;should&lt;/em&gt; behave.&lt;/p&gt;</content><category term="software development"></category></entry><entry><title>Click Prediction with Vowpal Wabbit</title><link href="https://late.am/post/2015/06/01/click-prediction.html" rel="alternate"></link><published>2015-06-01T00:00:00-04:00</published><updated>2015-06-02T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2015-06-01:/post/2015/06/01/click-prediction.html</id><summary type="html">&lt;p&gt;My co-worker &lt;a href="http://tech.magnetic.com/author/sam-steingold.html"&gt;Sam
Steingold&lt;/a&gt; and I have a
&lt;a href="http://tech.magnetic.com/2015/06/click-prediction-with-vowpal-wabbit.html"&gt;new
article&lt;/a&gt;
at the &lt;a href="http://tech.magnetic.com/"&gt;Magnetic engineering blog&lt;/a&gt; about how we
predict clicks in real-time online advertising.&lt;/p&gt;</summary><content type="html">&lt;p&gt;My co-worker &lt;a href="http://tech.magnetic.com/author/sam-steingold.html"&gt;Sam
Steingold&lt;/a&gt; and I have a
&lt;a href="http://tech.magnetic.com/2015/06/click-prediction-with-vowpal-wabbit.html"&gt;new
article&lt;/a&gt;
at the &lt;a href="http://tech.magnetic.com/"&gt;Magnetic engineering blog&lt;/a&gt; about how we
predict clicks in real-time online advertising.&lt;/p&gt;</content><category term="data science"></category><category term="python"></category></entry><entry><title>Optimize Python with Closures</title><link href="https://late.am/post/2015/05/07/optimize-python-with-closures.html" rel="alternate"></link><published>2015-05-07T00:00:00-04:00</published><updated>2015-06-13T10:41:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2015-05-07:/post/2015/05/07/optimize-python-with-closures.html</id><summary type="html">&lt;p&gt;Magnetic's real-time bidding system, written in pure Python, needs to keep up
with a tremendous volume of incoming requests. On an ordinary weekday, our
application handles about 300,000 requests per second at peak volumes, and
responds in under 10 milliseconds. It should be obvious that at this scale
optimizing the performance of the hottest sections of our code is of utmost
importance. This is the story of the evolution of one such hot section over
several performance-improving revisions.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Magnetic's real-time bidding system, written in pure Python, needs to keep up
with a tremendous volume of incoming requests. On an ordinary weekday, our
application handles about 300,000 requests per second at peak volumes, and
responds in under 10 milliseconds. It should be obvious that at this scale
optimizing the performance of the hottest sections of our code is of utmost
importance. This is the story of the evolution of one such hot section over
several performance-improving revisions.&lt;/p&gt;


&lt;h1&gt;Real Time Bidding&lt;/h1&gt;
&lt;p&gt;Real Time Bidding, or RTB, is a technique by which many internet ads delivered.
When you visit a website using RTB, a request is sent to dozens or hundreds of
"bidders" which have to quickly decide whether they want to show you an ad, and
if so, how much they would like to pay. RTB bidders have up to 100 milliseconds
to make this decision -- any slower and you won't win the auction no matter how
much you bid -- so performance is key.&lt;/p&gt;
&lt;p&gt;At Magnetic, we do much of our targeting in real time during the bid request. We
use a combination of filters to qualify ad campaigns for the particulars of the
bid request. Here we'll consider just one of the several types of filters we
use, one which checks that the content category of the page where an ad will be
shown against a set of categories that we want to target.&lt;/p&gt;
&lt;p&gt;Of course, different campaigns have different targeting criteria, and thus
different filter configurations. Additionally, each campaign may use a different
subset of filters. On average, we end up calling about 150 filters per bid
request, or 4.5 million filters per second total at peak times, so ensuring
maximal performance is essential.&lt;/p&gt;
&lt;h1&gt;First Approach: Classes&lt;/h1&gt;
&lt;p&gt;The obvious way to implement such a filter is to use a class to store the
configuration, thus allowing multiple instances of the filter for different
campaigns. At bidding time, we look up the set of instances which represent the
filters for each of the campaigns the user was targeted for, and call each
filter to determine if the user is eligible for the campaign. Such a class might
look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PageCategoryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;whitelist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt;
                &lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This code would pass most code reviews, and is a fairly straightforward
implementation of the idea we've thus far described only in prose. (We pass in a
dictionary of configuration, rather than direct arguments, since we have
different types of filters and want all of them to expose the same interface to
the code that sets them up and calls them.)&lt;/p&gt;
&lt;p&gt;Unfortunately, though perhaps unexpectedly given the topic of this post, there
is a performance problem with this approach, especially for a block of code that
will be called millions of times per second.&lt;/p&gt;
&lt;h2&gt;The Bound Method Problem&lt;/h2&gt;
&lt;p&gt;Superficially, accessing the &lt;code&gt;categories&lt;/code&gt; attribute and &lt;code&gt;filter&lt;/code&gt; method appear
to be doing about the same amount of work -- both access the attribute of an
instance. Unfortunately, looks can be deceiving:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;a_filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PageCategoryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;whitelist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;a_filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;categories&lt;/span&gt;
&lt;span class="go"&gt;frozenset([&amp;#39;baz&amp;#39;, &amp;#39;foo&amp;#39;, &amp;#39;bar&amp;#39;])&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;a_filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;
&lt;span class="go"&gt;&amp;lt;bound method PageCategoryFilter.filter of &amp;lt;PageCategoryFilter object at 0x107f13910&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As expected, &lt;code&gt;categories&lt;/code&gt; returns the attribute's value, but accessing &lt;code&gt;filter&lt;/code&gt;
returns a bound method object. What is a bound method? It's the magic that
allows Python to insert the &lt;code&gt;self&lt;/code&gt; argument when you call the method.&lt;/p&gt;
&lt;p&gt;Specifically, an instance's methods are access via a descriptor, a Python
feature that allows some code to be executed to satisfy the results of an
attribute access expression. When Python executes the definition of a class, it
wraps each function in a descriptor whose job is to supply the &lt;code&gt;self&lt;/code&gt; argument.
Later, when you access the attribute for the method, Python calls &lt;code&gt;__get__&lt;/code&gt; on
the descriptor, and supplies the instance as an argument. This allows the method
descriptor to rewrite the call to the underlying function to include the &lt;code&gt;self&lt;/code&gt;
argument. For more, see &lt;a href="http://chrisbeaumont.org/"&gt;Christ Beaumont&lt;/a&gt;'s excellent
&lt;a href="http://nbviewer.ipython.org/urls/gist.github.com/ChrisBeaumont/5758381/raw/descriptor_writeup.ipynb"&gt;Python Descriptors
Demystified&lt;/a&gt;,
or watch a short &lt;a href="https://www.youtube.com/watch?v=9A1ycvcgc_U"&gt;video version&lt;/a&gt; of
it.&lt;/p&gt;
&lt;p&gt;From a performance perspective, the key difference between ordinary attribute
access and method calls is that each time you call a method, or rather each time
you access the attribute which refers to a method, the Python VM must execute
some additional code to create the bound method. It must do more work in order
to provide the &lt;code&gt;self&lt;/code&gt; argument. None of this extra work is ordinarily visible --
it doesn't show up in stack traces, for instance -- but it does take some small
amount time to execute, and if you call methods often enough, that can add up.&lt;/p&gt;
&lt;h1&gt;Second Approach: Functions&lt;/h1&gt;
&lt;p&gt;If bound methods are a problem, perhaps we can arrange for all the pertinent
arguments just to be passed in to an ordinary function. This should avoid the
overhead of supplying the &lt;code&gt;self&lt;/code&gt; attribute:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;page_category_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;whitelist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt;
            &lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As we'll see in benchmarks, this code slightly outperforms the class-based
implementation by avoiding the need for bound method calls. But is this as fast
as we can make the filter go?&lt;/p&gt;
&lt;h2&gt;The Dictionary Access Problem&lt;/h2&gt;
&lt;p&gt;In either branch of this function, we do three dictionary accesses: one for
"mode", one for the "categories" item in the bid request dictionary; and one
for the "categories" item in the configuration dictionary. Since none of these
accesses is repeated in any individual call of this function, it makes no sense
to assign the result of the dictionary accesses to a local variable and thus
"cache" the result.&lt;/p&gt;
&lt;p&gt;"But wait," you say, "aren't dictionaries fast?"&lt;/p&gt;
&lt;p&gt;Ordinarily, and algorithmically, yes, dictionaries are quite fast. However,
hidden behind those square brackets is quite a bit of work: Python must hash the
key, apply a bit-mask to the hash, and look for the item in an array that
represents the storage for the dictionary. Edge cases can create collisions
which take even longer to resolve. For a more thorough exploration of how
dictionaries are implemented in Python, see &lt;a href="http://rhodesmill.org/brandon/"&gt;Brandon
Rhodes&lt;/a&gt;' talk &lt;a href="http://rhodesmill.org/brandon/talks/#mighty-dictionary"&gt;The Mighty
Dictionary&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In practice, for frequently-called code the cost of dictionary item access,
however small, is amplified and adds up.&lt;/p&gt;
&lt;h1&gt;Final Approach: Closures&lt;/h1&gt;
&lt;p&gt;So if even dictionaries are too slow, how can we resolve this performance
crisis? We can turn the "variables" stored in the dictionary into local
variables (sort of), which are the fastest to access since they are stored in an
array and referenced by pre-computed offset. Accessing a local variable in
Python is nearly instantaneous, or as close to it as we can get writing Python
code.&lt;/p&gt;
&lt;p&gt;A closure is a function which uses variables defined in its enclosing scope -- a
nested function. Python recognizes that the (inner) function uses variables that
aren't in its argument list, and makes those variables available to the inner
function. Such an inner function is said to "close over" the variables it uses
from the outer scope, hence "closure".&lt;/p&gt;
&lt;p&gt;Because the number and names of closures is known at compile time, the VM can
use an array to track the closed-over variables, in the same way as it uses an
array to track local variables and constants. For more on how Python builds and
executes a function, see &lt;a href="http://late.am/post/2012/03/26/exploring-python-code-objects.html"&gt;Exploring Python Code
Objects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We translate our page category filter into a closure by creating a factory
function which returns another function, the closure itself. From the
perspective of the calling code, this looks and feels a lot like creating an
instance of a class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_page_category_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;page_category_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;whitelist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt;
                &lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;page_category_filter&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;As we'll see, this implementation outperforms either of the others, because we
avoid two of the three dictionary lookups, and replaces them with fast access to
the closed-over variables &lt;code&gt;categories&lt;/code&gt; and &lt;code&gt;mode&lt;/code&gt;. Additionally, callers don't
suffer from the bound method problem, since &lt;code&gt;page_category_filter&lt;/code&gt; is just a
regular function with no magically inserted arguments.&lt;/p&gt;
&lt;h1&gt;The Proof is in the Timing&lt;/h1&gt;
&lt;p&gt;Each of the three methods shown here is "fast" in the sense that filtering any
single bid request takes very little actual time -- less than a microsecond on
my machine in all cases. However, since this code (and other code like it) is
called inside of what amounts to very tight loops, even tiny differences in the
run time of a single invocation amount to a lot overall.&lt;/p&gt;
&lt;p&gt;To fully exercise the code paths, we'll create four filter configurations: a
whitelist with some allowed categories, a blacklist with some forbidden
categories, an empty whitelist, and an empty blacklist. We'll call each of them
an equal number of times in a straightforward timing loop. Here's an example
using the first implementation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;PageCategoryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;whitelist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="n"&gt;PageCategoryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;blacklist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bar&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
    &lt;span class="n"&gt;PageCategoryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;whitelist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[])),&lt;/span&gt;
    &lt;span class="n"&gt;PageCategoryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;blacklist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;categories&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[])),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;bid_request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;categories&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bat&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a_filter&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;a_filter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bid_request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Averaged over 15 million calls to each filter in CPython 2.7.9 on my 2013
MacBook Pro, 2.6GHz Core i7, I get:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Per-Call Time&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Class&lt;/td&gt;
&lt;td&gt;0.4082 us&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Function&lt;/td&gt;
&lt;td&gt;0.3744 us&lt;/td&gt;
&lt;td&gt;8.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Closure&lt;/td&gt;
&lt;td&gt;0.3213 us&lt;/td&gt;
&lt;td&gt;21.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Feel free to check out &lt;a href="https://gist.github.com/dcrosta/3f60796682aa6a72b003"&gt;the benchmark
script&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What about PyPy?&lt;/h2&gt;
&lt;p&gt;At Magnetic, we've switched from running all of our performance-sensitive Python
code from CPython to PyPy, so let's consider whether these code optimizations
are still appropriate. I used the same benchmark setup as above, but with PyPy
2.5.1 and a warmup run for each implementation to allow PyPy time to JIT:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Per-Call Time&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Class&lt;/td&gt;
&lt;td&gt;0.0432 us&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Function&lt;/td&gt;
&lt;td&gt;0.0554 us&lt;/td&gt;
&lt;td&gt;-28.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Closure&lt;/td&gt;
&lt;td&gt;0.0431 us&lt;/td&gt;
&lt;td&gt;0.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;PyPy is incredibly fast, around an order of magnitude faster than CPython in
this benchmark. Because PyPy performs more advanced optimizations than CPython,
including many optimizations for classes and methods, the timings for the class
vs. closure implementations are a statistical tie.&lt;/p&gt;
&lt;p&gt;Curiously, the function implementation is actually slower than the class
approach in PyPy. PyPy is able to optimize code using classes more than code
using dictionaries, because it is able to specialize code for each class in your
program. Dictionaries, on the other hand, are a generic mapping data structure,
and PyPy is not able to create specialized machine code for one particular
"shape" of dictionary or another. &lt;a href="https://alexgaynor.net/"&gt;Alex Gaynor&lt;/a&gt; covers
this distinction in greater depth in his talk &lt;a href="http://pyvideo.org/video/2627/fast-python-slow-python"&gt;Fast Python, Slow
Python&lt;/a&gt; from PyCon 2014.&lt;/p&gt;
&lt;h1&gt;So what have we learned?&lt;/h1&gt;
&lt;p&gt;The &lt;a href="http://legacy.python.org/dev/peps/pep-0020/"&gt;Zen of Python&lt;/a&gt; implores us to
implement our code in the "one -- and preferably only one -- obvious way", which
is likely one of the first two approaches presented here (depending on whether
you favor functions or classes). Indeed, this is still good advice, as code is
read far more often than it is written. However, there are cases where less
obvious ways have distinct benefits. Or maybe I'm just Dutch.&lt;/p&gt;
&lt;p&gt;We also must always remember our
&lt;a href="http://en.wikipedia.org/wiki/Donald_Knuth"&gt;Knuth&lt;/a&gt;: "&lt;a href="http://c2.com/cgi/wiki?PrematureOptimization"&gt;premature optimization is
the root of all evil&lt;/a&gt;." At
Magnetic, we pursued this optimization only because it made sense for our
application, where the filters are called very frequently, and only after
measurement showed that the real-time filters accounted for a large amount of
our per-request processing time.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Many thanks to &lt;a href="http://emptysqua.re/"&gt;A. Jesse Jiryu Davis&lt;/a&gt; for reviewing
a draft of this article.&lt;/em&gt;&lt;/p&gt;</content><category term="python"></category><category term="performance"></category></entry><entry><title>Good Test, Bad Test</title><link href="https://late.am/post/2015/04/20/good-test-bad-test.html" rel="alternate"></link><published>2015-04-20T00:00:00-04:00</published><updated>2016-05-02T07:55:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2015-04-20:/post/2015/04/20/good-test-bad-test.html</id><summary type="html">&lt;p&gt;A good test suite is a developer's best friend -- it tells you what your
code does and what it's supposed to do. It's your second set of eyes as
you're working, and your safety net before you go to production.&lt;/p&gt;
&lt;p&gt;By contrast, a bad test suite stands in the way of progress -- whenever you
make a small change, suddenly fifty tests are failing, and it's not clear
how or why the cases are related to your change.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;A good test suite is a developer's best friend -- it tells you what your
code does and what it's supposed to do. It's your second set of eyes as
you're working, and your safety net before you go to production.&lt;/p&gt;
&lt;p&gt;By contrast, a bad test suite stands in the way of progress -- whenever you
make a small change, suddenly fifty tests are failing, and it's not clear
how or why the cases are related to your change.&lt;/p&gt;


&lt;p&gt;But how do you know if your test suite is "good"? At &lt;a href="https://us.pycon.org/2015/"&gt;PyCon US
2015&lt;/a&gt; I gave a
&lt;a href="https://speakerdeck.com/dcrosta/good-test-bad-test"&gt;talk&lt;/a&gt; that addressed
three common myths about testing, which lead down a path of painful testing.&lt;/p&gt;
&lt;p&gt;The talk, and this article, are heavily opinionated. As with all opinions,
it's up to you, the reader, to decide whether they're applicable to your
particular situation or not. In other words, you still need to use your
judgement.&lt;/p&gt;
&lt;h1&gt;What's a "Good" Test?&lt;/h1&gt;
&lt;p&gt;I'll define a good test (and by extension a good test suite) as one that has
the following virtues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fast&lt;/strong&gt;. If a test is slow, developers won't run it locally. This will
  lead to broken CI builds, and more time spent fixing the code that broke
  the test afterwards, more context switching, lack of flow, and all the
  concomitant problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selective&lt;/strong&gt;. If a bug is introduced into the code under test, only one
  or a few test cases should fail. If all cases start failing, then it will
  require a too much time to figure out exactly what is wrong. Your tests
  should point you in the direction of what's wrong.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Repeatable&lt;/strong&gt;. Tests should always give the same result if the code
  being tested hasn't changed. Any reliance in a test on timing, randomness,
  or external state (databases, the date or time, etc) will lead to wasted
  time spent investigating spurious failures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reliable&lt;/strong&gt;. Your test should pass when the code is working, and fail
  when the code has a bug. Perhaps this is self-evident, but it bears
  repeating -- a test should not give false positives or false negatives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Helpful&lt;/strong&gt;. In particular, test &lt;em&gt;error messages&lt;/em&gt; should be helpful. Seeing
  "user_has_logged_in() was False after user login" tells you exactly what's
  wrong, whereas "AssertionError: False is not True" requires you to figure
  it out for yourself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As we'll see, the myths, and the mistakes they cause developers to make
while writing tests, come from good intentions, but can lead to violations
of one or more of the above principles.&lt;/p&gt;
&lt;h1&gt;Myth 1: 100% Coverage&lt;/h1&gt;
&lt;p&gt;This myth is pernicious primarily because of how common it is. Perhaps a
team adopts code coverage monitoring after a few bouts with bugs that could
easily have been prevented with tests, and there's a land rush to improve
coverage. More tests must be better, right?&lt;/p&gt;
&lt;p&gt;Beware: danger lurks in too-eagerly chasing full coverage. The truth of the
matter is, there are some tests that simply shouldn't be written: they add
too much complexity to your codebase, and provide very little value.&lt;/p&gt;
&lt;p&gt;For example, consider tests that exercise parts of your application that
interact with some external resource, not under your control, like a third
party API your application works with:&lt;/p&gt;
&lt;p&gt;&lt;img alt="There's a reason we draw it as a
cloud..." src="/static/good-test-internet-cloud.png"&gt;&lt;em&gt;There's a reason we draw it
as a cloud...
(&lt;a href="https://www.sparkholder.com/wp-content/uploads/2015/01/sparkholder-api.png"&gt;source&lt;/a&gt;,
modified)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Notice that thing in the middle. There's a reason we draw it as a cloud: we
don't really know what happens in there. &lt;em&gt;Anything&lt;/em&gt; could happen in there.
You might get a very slow connection; you might get corrupted bytes back
that don't make any sense; you might simply shout into the void, never to
hear back from the third party; or you might be on a plane, with no access
to the internet. Since you can't control it, you're totally subject to the
whims of the internet and of the third party.&lt;/p&gt;
&lt;p&gt;I call tests that rely on third parties in this way &lt;a href="http://en.wikipedia.org/wiki/Mutual_assured_destruction"&gt;"Mutually-Assured
Destruction"&lt;/a&gt;
tests. Ordinarily, the external service is operating normally and your tests
pass. But occasionally, something outside of your control is broken, and
suddenly your test suite is reporting a bug that doesn't exist in your code.
Either both sides work, or both sides fail, hence "MAD".&lt;/p&gt;
&lt;p&gt;Perhaps this is controversial, but I recommend that you not write tests for
this kind of code in the first place. It's failures don't tell you anything
interesting or actionable about your codebase.&lt;/p&gt;
&lt;p&gt;Instead, try to minimize the amount of MAD code, and make it as dumb as
possible. Don't put business logic that needs thorough testing in-line with
third party integrations, and make sure your integrations can report errors
back to you. Be sure to test your exception reporting service, and keep
notification rules up to date. If you depend on an external service for
critical functionality, you can't ensure that it will always be available
and correct; but you can be sure that you're notified when it goes down, and
have a plan in place to mitigate the impact.&lt;/p&gt;
&lt;h1&gt;Myth 2: Assert About Everything&lt;/h1&gt;
&lt;p&gt;Assertions are how tests tell us whether our code is working or not. Without
assertions, tests literally wouldn't do anything useful. So, if we add more
assertions, we'll increase our confidence that our test does what we want it
to do, right?&lt;/p&gt;
&lt;p&gt;Too many assertions, and especially the wrong kinds of assertions create a
maintenance hazard in your test suite. Let's illustrate the point with an
example, a function which parses a log line from a web server in a
hypothetical pipe-separated format, returning a dictionary:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_log_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2015-03-11T20:09:25|GET /foo?bar=baz|...&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;parsed_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It is tempting to use the familiar &lt;code&gt;assertEqual&lt;/code&gt; with a dictionary whose
contents are identical to the parsed result we expect:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_log_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2015-03-11T20:09:25|GET /foo?bar=baz|...&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;parsed_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2015&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bar=baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;parsed_dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This will certainly work, assuming the parse function is implemented
correctly. But what kind of error messages will we get from this test?
Take a look:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nl"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;date&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2015&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/foo&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;method&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;G [truncated]... != {&amp;#39;&lt;/span&gt;&lt;span class="nc"&gt;date&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;: datetime.datetime(2015, 3, 11, 20, 9, 25), &amp;#39;&lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;: &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="vm"&gt;?&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;, &amp;#39;&lt;/span&gt;&lt;span class="k"&gt;method&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;: &amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;truncated&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Can you see what the error is? No? Neither can I. Fortunately, we can fix
this fairly easily:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_log_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;2015-03-11T20:09:25|GET /foo?bar=baz|...&amp;quot;&lt;/span&gt;

    &lt;span class="n"&gt;parsed_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse_line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2015&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;parsed_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;date&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar=baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed_dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now when our test fails, we can see exactly what's going on:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/foo&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/foo?&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We fix the bug, and move on with our lives. Eventually, we'll expand the
functionality of the parser, and add more test cases. Inevitably, these
assertions will be copied and pasted, with some modification:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_get_request_log_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar=baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_post_request_log_lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;POST&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar=baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_log_lines_with_oof_requests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/oof&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar=baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Notice that only one assertion is actually changing in each test, but we're
repeating most of the others. This means that if we break one feature -- say
request path parsing -- we'll get failures from most of our test cases. In
order to figure out what's actually broken, we'll have to dig through each
failure and think hard about each test case to figure out what to fix.&lt;/p&gt;
&lt;p&gt;You should ask yourself, for each test case, "What would I do when this test
case fails?" We can minimize the effort required to transate a test failure
into a fix by following one simple rule: make only one assertion per test
case. Yup, only one.&lt;/p&gt;
&lt;p&gt;Let's rewrite this test suite using the new rule:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_request_method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GET&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;method&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_request_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_parses_query_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;bar=baz&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_gives_none_when_no_query_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertIsNone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parsed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now, when we break request path parsing, we'll only get one test case
failure, and the test case name and failure traceback will point us directly
at what we need to fix: the request path parsing portion of our code.&lt;/p&gt;
&lt;p&gt;When you adopt this style of test design, you'll also find that the way you
write your test cases changes. Before, we had test cases representing some
special cases we expect in the real world: an "oof" request, a POST request,
etc.&lt;/p&gt;
&lt;p&gt;Now, our test cases reflect the orthogonal behaviors of the code under test:
it parses the request method, path, query string, etc. Not only are the test
failures more instructive, but our test cases now serve as better
documentation: scanning the names tells us what the &lt;code&gt;parse_line&lt;/code&gt; function
actually does (and, by extension, what it doesn't do).&lt;/p&gt;
&lt;h1&gt;Myth 3: Mock Makes Tests Better&lt;/h1&gt;
&lt;p&gt;&lt;a href="http://www.voidspace.org.uk/python/mock/"&gt;Mock&lt;/a&gt; is a powerful library for
simulating and replacing Python objects in tests. You can safely
monkey-patch, check what calls were made against your mocks, raise
exceptions, and more.&lt;/p&gt;
&lt;p&gt;Common reasons for using mock are to reduce dependence on external, possibly
slow resources, like databases, to isolate testing to one layer of your
code, or to control "inputs" to a method or function that come from some
nested call. Mock is certainly capable of doing all of these things.&lt;/p&gt;
&lt;p&gt;But with great power comes great responsibility -- used unwisely, mock can
wreak more havoc on your testing than the thing you were trying to replace
would have. Take, as an example, a suite of tests on a web application with
login functionality:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Returns True if the user is authenticated.&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# WARNING: Don&amp;#39;t use this code in production!&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;username&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# do more stuff ...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Since we want to test the behavior of the &lt;code&gt;login&lt;/code&gt; method under two scenarios
-- when the user is authenticated, and when the user is not. We might use
mock to control the return value of the internal call to &lt;code&gt;authenticate&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_returns_200_on_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DBClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dcrosta&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_it_returns_401_on_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;DBClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;return_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dcrosta&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Now we'll add a new feature to our application -- user permissions. For
simplicity, let's have the &lt;code&gt;authenticate&lt;/code&gt; method return not only whether the
user is authenticated, but what roles in our application the user has:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Returns a tuple of (authenticated, list_of_roles).&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="c1"&gt;# WARNING: Still don&amp;#39;t use this code in production!&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;admin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If we stop here, we've introduced a bug into our code: we haven't updated
&lt;code&gt;login&lt;/code&gt; to handle the new return value from &lt;code&gt;authenticate&lt;/code&gt;. But our test
suite isn't telling us that, because the test mocks still supply the old
format of return value (just a boolean).&lt;/p&gt;
&lt;p&gt;In the worst case, you might not notice this bug until it has been in
production for some time, since this particular error won't raise any
exceptions (a non-empty tuple is considered truthful in Python).&lt;/p&gt;
&lt;p&gt;Whenever you write a test, you have to consider: what would cause this test
case to &lt;em&gt;pass&lt;/em&gt; when it ought to &lt;em&gt;fail&lt;/em&gt;? With mock, this can happen whenever
the object you're replacing has changed.&lt;/p&gt;
&lt;p&gt;The solution for cases like this is to use a "verified double" -- a
replacement object that closely, hopefully identically, emulates the
behavior of the thing you're replacing. Here's a double for the &lt;code&gt;DBClient&lt;/code&gt;
class we were previously mocking:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StubDBClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Had we been using this double all along, the test cases might have looked
like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_login_returns_200_on_success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StubDBClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dcrosta&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dcrosta&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_login_returns_401_on_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StubDBClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dcrosta&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Not only are the tests more readable -- they demonstrate the full set of
actions that have to take place to achieve the result being verified -- but
the test cases now demonstrate that we've got a bug in our code until we
update the &lt;code&gt;login&lt;/code&gt; method to understand the new return value from
&lt;code&gt;authenticate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Since the double itself isn't a one-liner, you're likely to share the double
across all test cases that use this functionality. Now you only hvae one
place in your tests to update when you change the interface of the real
object. Once you change it, your test suite will tell you all the other
areas you need to revisit. Once the tests pass, your refactor is complete.&lt;/p&gt;
&lt;h1&gt;Final Thoughts&lt;/h1&gt;
&lt;p&gt;Too often, testing is an afterthought. The exciting work that we all crave
is to build the actual working code. But no code is created in an eternally
perfect and pristine state. We will eventually have to modify it to add or
remove functionality, to address performance concerns, to work with new
frameworks, etc. Tests are our safety net when we perform these operations
on a codebase: they tell us what we've broken, and what we have to fix.&lt;/p&gt;
&lt;p&gt;It's important to acknowledge that tests can smell just as well as real
code. We deal with code smell in a test suite the same as we would in any
other type of code -- with thought and care. A well designed test suite, one
which lives up to the virtues of good testing, will be your best friend in a
long-lived codebase.&lt;/p&gt;</content><category term="testing"></category><category term="python"></category><category term="pycon"></category><category term="eat your vegetables"></category></entry><entry><title>Crickets</title><link href="https://late.am/post/2015/03/31/crickets.html" rel="alternate"></link><published>2015-03-31T00:00:00-04:00</published><updated>2015-03-31T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2015-03-31:/post/2015/03/31/crickets.html</id><summary type="html">&lt;p&gt;It's been real quiet around here. Like, tumbleweed-rolling-by quiet.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;It's been real quiet around here. Like, tumbleweed-rolling-by quiet.&lt;/p&gt;


&lt;p&gt;&lt;img alt="Tumbleweed." src="/static/tumbleweed.jpg"&gt;
&lt;em&gt;&lt;a href="https://lessonslearntlastnight.wordpress.com/2013/03/10/lesson-184-tumbleweeds-are-cooler-than-your-boyfriend-fact/"&gt;Lessonslearnedlastnight&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I've been lazy. I'm going to change that. We've started a &lt;a href="http://tech.magnetic.com/"&gt;tech blog at
work&lt;/a&gt;, and it's gotten me back in the mood to
blog. I'll be cross-posting a few articles, and also posting some original
work here as time permits.&lt;/p&gt;
&lt;p&gt;I've also updated the blog to use &lt;a href="http://docs.getpelican.com/"&gt;Pelican&lt;/a&gt;,
which might have broken your feeds, in case anyone reads that way. My
apologies if you suddenly get a slew of duplicates.&lt;/p&gt;</content></entry><entry><title>How to Rebuild a Virtualenv in Heroku</title><link href="https://late.am/post/2012/09/22/how-to-rebuild-a-virtualenv-in-heroku.html" rel="alternate"></link><published>2012-09-22T00:00:00-04:00</published><updated>2012-09-22T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2012-09-22:/post/2012/09/22/how-to-rebuild-a-virtualenv-in-heroku.html</id><summary type="html">&lt;p&gt;Today I managed to get myself a little bit stuck with Heroku's otherwise seamless Python support. In an earlier revision of my app, I had used &lt;code&gt;-e&lt;/code&gt; to install an editable version of a package (the version of the package I needed had not yet been released to PyPI, so I pointed pip at Github). Since then, the version I need has been released.&lt;/p&gt;
&lt;p&gt;So, update &lt;code&gt;requirements.txt&lt;/code&gt;, then &lt;code&gt;git push heroku master&lt;/code&gt;, and all is well, right? Not so fast...&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Today I managed to get myself a little bit stuck with Heroku's otherwise seamless Python support. In an earlier revision of my app, I had used &lt;code&gt;-e&lt;/code&gt; to install an editable version of a package (the version of the package I needed had not yet been released to PyPI, so I pointed pip at Github). Since then, the version I need has been released.&lt;/p&gt;
&lt;p&gt;So, update &lt;code&gt;requirements.txt&lt;/code&gt;, then &lt;code&gt;git push heroku master&lt;/code&gt;, and all is well, right? Not so fast...&lt;/p&gt;


&lt;p&gt;Here's what actually happened:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/tmp/.../pip-1.1-py2.7.egg/pip/basecommand.py&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/tmp/.../pip-1.1-py2.7.egg/pip/commands/install.py&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;requirement_set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;install_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;global_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/tmp/.../pip-1.1-py2.7.egg/pip/req.py&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;requirement&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uninstall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto_confirm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/tmp/.../pip-1.1-py2.7.egg/pip/req.py&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link_pointer&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Egg-link &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; does &amp;#39;&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;not match installed location of &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt; (at &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;link_pointer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="ne"&gt;AssertionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Egg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt; &lt;span class="o"&gt;../../../../&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="n"&gt;does&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;
&lt;span class="n"&gt;installed&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tmp&lt;/span&gt;&lt;span class="o"&gt;/.../&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;It turns out this is a &lt;a href="https://github.com/pypa/pip/pull/641"&gt;known bug in Pip&lt;/a&gt;, and will hopefully soon be fixed. Unfortunately, I don't think I can use pip to install an editable version of pip which fixes the bug in which pip can't uninstall editable versions of packages on Heroku. (Sorry for that sentence).&lt;/p&gt;
&lt;p&gt;So I did what any good internet citizen would do: &lt;a href="https://twitter.com/lazlofruvous/status/249593313173331968"&gt;tweet first&lt;/a&gt;, google later. Google turned up &lt;a href="https://github.com/heroku/heroku-buildpack-python/pull/47"&gt;a patch to the Heroku Python buildpack&lt;/a&gt; that was merged several months ago, and appeared to do the right thing -- sadly, there was no apparent way to get the &lt;code&gt;CLEAN_VIRTUALENV&lt;/code&gt; environment variable to the compile phase of my app.&lt;/p&gt;
&lt;p&gt;Fortunately, at this point my friend &lt;a href="https://twitter.com/dirnonline"&gt;Andy&lt;/a&gt; saw the tweet, and provided the critical hint: the &lt;a href="https://devcenter.heroku.com/articles/labs-user-env-compile"&gt;user-env-compile&lt;/a&gt; Heroku labs feature. This gave me a way to ask the buildpack compiler to create a new virtualenv for me. On my next &lt;code&gt;git push heroku master&lt;/code&gt;, I got a nice message: "CLEAN_VIRTUALENV set, rebuilding virtualenv" -- however, the same bug as before when installing requirements :'(&lt;/p&gt;
&lt;p&gt;I reviewed the patch more closely, then traced through the buildpack file, and found that the &lt;code&gt;VIRTUALENV_DIRS&lt;/code&gt; variable that the compiler uses to clean up the virtualenv is never set, so nothing was being deleted, and despite the message, my virtualenv was not being cleared and recreated. However, since the compiler is just a shell script, the same trick that got &lt;code&gt;CLEAN_VIRTUALENV&lt;/code&gt; to the compiler could also be used to set &lt;code&gt;VIRTUALENV_DIRS&lt;/code&gt;. (And I've &lt;a href="https://github.com/heroku/heroku-buildpack-python/pull/55"&gt;submitted a patch&lt;/a&gt; so that in the future this won't be necessary.)&lt;/p&gt;
&lt;p&gt;So, to put it all together, here's what you need to do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ heroku labs:add user-env-compile
$ heroku config:set &lt;span class="nv"&gt;CLEAN_VIRTUALENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yes
$ heroku config:set &lt;span class="nv"&gt;VIRTUALENV_DIRS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;.heroku/venv
$ git push heroku master
$ heroku config:unset CLEAN_VIRTUALENV
$ heroku config:unset VIRTUALENV_DIRS
$ heroku labs:disable user-env-compile
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Hope this saves someone some time!&lt;/p&gt;</content><category term="python"></category><category term="heroku"></category><category term="virtualenv"></category></entry><entry><title>A Modest Proposal for Tech Conferences</title><link href="https://late.am/post/2012/07/28/a-modest-proposal-for-tech-conferences.html" rel="alternate"></link><published>2012-07-28T00:00:00-04:00</published><updated>2012-07-28T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2012-07-28:/post/2012/07/28/a-modest-proposal-for-tech-conferences.html</id><summary type="html">&lt;p&gt;After &lt;a href="http://twitter.com/lazlofruvous/status/228929543501656064"&gt;more&lt;/a&gt; &lt;a href="http://twitter.com/lazlofruvous/status/229147093539880960"&gt;difficulty&lt;/a&gt; &lt;a href="http://twitter.com/lazlofruvous/status/229229334953598976"&gt;than expected&lt;/a&gt;, I'm at &lt;a href="http://pyohio.org/"&gt;PyOhio&lt;/a&gt;, and once again I found myself not taking notes during today's sessions, but madly scrambling to open browser tabs to each of the open source projects, blog posts, and other online resources the presenters mentioned. It's a rush to keep up, particularly with laundry-list-of-links slides!&lt;/p&gt;
&lt;p&gt;The sessions at PyOhio (and most other conferences I attend) are recorded and posted online, but I never seem to make the time to look for the links after the fact -- and you can't click a link in a video of a projected slide anyway.&lt;/p&gt;
&lt;p&gt;So this has gotten me thinking: since presenters typically submit talks through web forms, why not also ask them for a listing of the resources that are a part of their presentation, and preserve this in the online talk notes? A few words on the subject of the link would be nice as well, but with not that much cleverness, this could be extracted from the page. Knowing that this will be available after the fact frees presenters to move fluidly through their presentations, and allows attendees to focus on the content rather than trying to type URLs as quickly as possible.&lt;/p&gt;
&lt;p&gt;Perhaps not everyone will be willing to take the time to fill out what, to them, surely seems like a busy-work form, but with the right application of social pressure, and after the expectation has been set that speakers &lt;em&gt;will&lt;/em&gt; do this, I suspect that a majority would be willing to make the effort.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;After &lt;a href="http://twitter.com/lazlofruvous/status/228929543501656064"&gt;more&lt;/a&gt; &lt;a href="http://twitter.com/lazlofruvous/status/229147093539880960"&gt;difficulty&lt;/a&gt; &lt;a href="http://twitter.com/lazlofruvous/status/229229334953598976"&gt;than expected&lt;/a&gt;, I'm at &lt;a href="http://pyohio.org/"&gt;PyOhio&lt;/a&gt;, and once again I found myself not taking notes during today's sessions, but madly scrambling to open browser tabs to each of the open source projects, blog posts, and other online resources the presenters mentioned. It's a rush to keep up, particularly with laundry-list-of-links slides!&lt;/p&gt;
&lt;p&gt;The sessions at PyOhio (and most other conferences I attend) are recorded and posted online, but I never seem to make the time to look for the links after the fact -- and you can't click a link in a video of a projected slide anyway.&lt;/p&gt;
&lt;p&gt;So this has gotten me thinking: since presenters typically submit talks through web forms, why not also ask them for a listing of the resources that are a part of their presentation, and preserve this in the online talk notes? A few words on the subject of the link would be nice as well, but with not that much cleverness, this could be extracted from the page. Knowing that this will be available after the fact frees presenters to move fluidly through their presentations, and allows attendees to focus on the content rather than trying to type URLs as quickly as possible.&lt;/p&gt;
&lt;p&gt;Perhaps not everyone will be willing to take the time to fill out what, to them, surely seems like a busy-work form, but with the right application of social pressure, and after the expectation has been set that speakers &lt;em&gt;will&lt;/em&gt; do this, I suspect that a majority would be willing to make the effort.&lt;/p&gt;
</content><category term="conferences"></category></entry><entry><title>What the heck is an xrange?</title><link href="https://late.am/post/2012/06/18/what-the-heck-is-an-xrange.html" rel="alternate"></link><published>2012-06-18T00:00:00-04:00</published><updated>2012-06-19T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2012-06-18:/post/2012/06/18/what-the-heck-is-an-xrange.html</id><summary type="html">&lt;p&gt;Pop quiz: how would you implement Python's &lt;code&gt;xrange&lt;/code&gt; (known in Python 3.x as &lt;code&gt;range&lt;/code&gt;) in Python and without making a list or any other sequence type?&lt;/p&gt;
&lt;p&gt;If you answered "with a generator," you'd be wrong! And it's likely because you've only ever used an &lt;code&gt;xrange&lt;/code&gt; in code like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# do something 10 times&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In this case (where the &lt;code&gt;xrange&lt;/code&gt; is only used as an "argument" to a &lt;code&gt;for&lt;/code&gt; loop), a generator would probably suffice. But, in fact, &lt;code&gt;xrange&lt;/code&gt; is implemented as an object, not a function or generator. Why? Let's find out.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Pop quiz: how would you implement Python's &lt;code&gt;xrange&lt;/code&gt; (known in Python 3.x as &lt;code&gt;range&lt;/code&gt;) in Python and without making a list or any other sequence type?&lt;/p&gt;
&lt;p&gt;If you answered "with a generator," you'd be wrong! And it's likely because you've only ever used an &lt;code&gt;xrange&lt;/code&gt; in code like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# do something 10 times&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In this case (where the &lt;code&gt;xrange&lt;/code&gt; is only used as an "argument" to a &lt;code&gt;for&lt;/code&gt; loop), a generator would probably suffice. But, in fact, &lt;code&gt;xrange&lt;/code&gt; is implemented as an object, not a function or generator. Why? Let's find out.&lt;/p&gt;


&lt;h1&gt;The Sequence Protocol&lt;/h1&gt;
&lt;p&gt;The &lt;code&gt;range&lt;/code&gt; builtin returns a list, which (as the &lt;em&gt;de facto&lt;/em&gt; sequence type) implements all the sequence protocol methods. &lt;code&gt;xrange&lt;/code&gt; was intended to replace &lt;code&gt;range&lt;/code&gt; (and did in Python 3.x), so it, too, must support all that a sequence supports.&lt;/p&gt;
&lt;p&gt;So, what does a sequence support? Sequences have a finite, ordered bunch of elements, which means they have a length (they support &lt;code&gt;len&lt;/code&gt;), they support indexed access (with the &lt;code&gt;[]&lt;/code&gt; operator), and most importantly, they can be iterated -- in forward order with a &lt;code&gt;for&lt;/code&gt; loop, and in reverse order with the help of the &lt;code&gt;reversed&lt;/code&gt; builtin. These behaviors are all implemented by way of "dunder" methods, and typically accessed using language syntax (as with &lt;code&gt;for ... in&lt;/code&gt;) or builtins (as with &lt;code&gt;len&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Sequences also support two instance methods: &lt;code&gt;index&lt;/code&gt;, which returns the 0-based position in the sequence of the first occurrence of the argument, and &lt;code&gt;count&lt;/code&gt;, which returns the number of occurrences of the argument.&lt;/p&gt;
&lt;p&gt;Perhaps surprisingly, we can actually implement all of this functionality with constant-time and constant-space operations, using only the bounds of the &lt;code&gt;xrange&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Building an &lt;code&gt;xrange&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;As a quick refresher, both &lt;code&gt;range&lt;/code&gt; and &lt;code&gt;xrange&lt;/code&gt; take between 1 and 3 arguments. A single argument defines the length of the sequence (with an implicit starting value of 0). Two arguments define the starting point and ending point (actually, the second argument is the next integer &lt;em&gt;after&lt;/em&gt; the last one that will be a part of the sequence). A third argument defines a step value, the number between each of the elements of the sequence. All arguments must be integers.&lt;/p&gt;
&lt;p&gt;With that in hand, we can begin building our implementation of &lt;code&gt;xrange&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Pure-Python implementation of an ``xrange`` (aka ``range``&lt;/span&gt;
&lt;span class="sd"&gt;    in Python 3) object. See `the CPython documentation&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;lt;http://docs.python.org/py3k/library/functions.html#range&amp;gt;`_&lt;/span&gt;
&lt;span class="sd"&gt;    for details.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xrange() requires 1-3 int arguments&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;an integer is required&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xrange() arg 3 must not be zero&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Update: thanks to &lt;a href="http://news.ycombinator.com/user?id=sltkr"&gt;sltkr&lt;/a&gt; for a correction to the &lt;code&gt;_len&lt;/code&gt; calculation on &lt;a href="http://news.ycombinator.com/item?id=4129658"&gt;Hacker News&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Most of this is error checking and adjustment of values for impossible sequences (like a sequence starting at 0 and going to 10 stepping by -1; in these cases, we collapse the &lt;code&gt;xrange&lt;/code&gt; to a zero-length sequence with the same start value).&lt;/p&gt;
&lt;p&gt;Importantly, since the entire sequence is "known" at initialization time, and is immutable, I precompute the length of the sequence, which is useful in the implementation of several of the methods, as we will see.&lt;/p&gt;
&lt;p&gt;With our computed &lt;code&gt;_start&lt;/code&gt;, &lt;code&gt;_stop&lt;/code&gt;, &lt;code&gt;_step&lt;/code&gt;, and &lt;code&gt;_len&lt;/code&gt; attributes in hand, we can implement equality, representation, and length trivially:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__repr__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xrange(&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;)&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xrange(&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;)&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;xrange(&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s1"&gt;)&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__eq__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; \
           &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; \
           &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; \
           &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Nothing really surprising here.&lt;/p&gt;
&lt;p&gt;Next we'll implement &lt;code&gt;index&lt;/code&gt;, as it serves as a basis for several other methods.&lt;/p&gt;
&lt;p&gt;To find the index of an element in an immaterial sequence, we have to do a little math. First, we compute the difference between the element we're looking for, and the start value for the sequence. Next, we compute the quotient and remainder using the &lt;code&gt;divmod&lt;/code&gt; builtin. If the remainder is 0, then the element may be a member of the sequence (it falls on one of the values that might be produced by adding some number of &lt;code&gt;_step&lt;/code&gt;s to the &lt;code&gt;_start&lt;/code&gt;). If the quotient is between 0 (inclusive) and the sequence's length (exclusive), then this is definitely a member, and we can return its index; if not, we raise &lt;code&gt;ValueError&lt;/code&gt; to indicate it was not found:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return the 0-based position of integer `value` in&lt;/span&gt;
&lt;span class="sd"&gt;    the sequence this xrange represents.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt;
    &lt;span class="n"&gt;quotient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;divmod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;quotient&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quotient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%r&lt;/span&gt;&lt;span class="s1"&gt; is not in range&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note that we're safe from &lt;code&gt;ZeroDivisionError&lt;/code&gt; in the &lt;code&gt;divmod&lt;/code&gt; call, since we've previously ensured that &lt;code&gt;_step&lt;/code&gt; is not zero.&lt;/p&gt;
&lt;p&gt;With this in hand, we can trivially implement &lt;code&gt;__contains__&lt;/code&gt; (the dunder method that is called by &lt;code&gt;x in foo&lt;/code&gt; syntax for checking membership):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__contains__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return ``True`` if the integer `value` occurs in&lt;/span&gt;
&lt;span class="sd"&gt;    the sequence this xrange represents.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Implementing &lt;code&gt;count&lt;/code&gt; is trivial, as well, since an element can only occur zero or one times in an &lt;code&gt;xrange&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return the number of ocurrences of integer `value`&lt;/span&gt;
&lt;span class="sd"&gt;    in the sequence this xrange represents.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Element access with the &lt;code&gt;[]&lt;/code&gt; operator (implemented with the &lt;code&gt;__getitem__&lt;/code&gt; dunder method) is relatively easy, except for two complications: we have to support negative indexes, for sequence access from the end; and we have to support slice access.&lt;/p&gt;
&lt;p&gt;The former is easy, as we can just add the length of the sequence to the index we're attempting to access, and proceed with bounds-checking as usual. Since the behavior of slices of sequences is somewhat subtle, we implement it in a separate method for clarity:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return the element at position ``index`` in the sequence&lt;/span&gt;
&lt;span class="sd"&gt;    this xrange represents, or raise :class:`IndexError` if the&lt;/span&gt;
&lt;span class="sd"&gt;    position is out of range.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__getitem_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# negative indexes access from the end&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xrange object index out of range&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slce&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return an xrange which represents the requested slce&lt;/span&gt;
&lt;span class="sd"&gt;    of the sequence represented by this xrange.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slce&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slce&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slce&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;slice step cannot be 0&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt;
    &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_stop&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;xrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;rv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;step&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;rv&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Finally, we come to iteration, which is (I suspect) what most people use &lt;code&gt;xrange&lt;/code&gt; objects for. Remember that &lt;code&gt;xrange&lt;/code&gt; objects act like any other sequence -- you can have multiple, independent iterators iterating the same underlying sequence, so we have to implement a separate object for the iterator. The implementation of the iterator is about as simple as you might expect: it tracks the last value it returned, and adds the &lt;code&gt;_step&lt;/code&gt; value to that value, then returns it for each element it iterates:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;xrangeiterator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Iterator&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;An iterator for an :class:`xrange`.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;xrangeobj&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_xrange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xrangeobj&lt;/span&gt;

        &lt;span class="c1"&gt;# Intialize the &amp;quot;last outputted value&amp;quot; to the value&lt;/span&gt;
        &lt;span class="c1"&gt;# just before the first value; this simplifies next()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_xrange&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_start&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_xrange&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__iter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;An iterator is already an iterator, so return ``self``.&lt;/span&gt;
&lt;span class="sd"&gt;        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Return the next element in the sequence represented&lt;/span&gt;
&lt;span class="sd"&gt;        by the xrange we are iterating, or raise StopIteration&lt;/span&gt;
&lt;span class="sd"&gt;        if we have passed the end of the sequence.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_last&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_xrange&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_step&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_xrange&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_len&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;StopIteration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_last&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Put it all together&lt;/h1&gt;
&lt;p&gt;I've published the code for the &lt;code&gt;xrange&lt;/code&gt; object, along with a comprehensive test suite, &lt;a href="https://github.com/dcrosta/xrange"&gt;to my GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Closing Thoughts&lt;/h1&gt;
&lt;p&gt;Python already has &lt;code&gt;xrange&lt;/code&gt; (or, in Python 3, &lt;code&gt;range&lt;/code&gt;), as a built-in, implemented efficiently in C, so re-implementing it in Python is largely a learning exercise, rather than code which you should actually use.&lt;/p&gt;
&lt;p&gt;However, this implementation is superior to CPython's implementation in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I have backported support for slicing, &lt;code&gt;index&lt;/code&gt;, and &lt;code&gt;count&lt;/code&gt; from Python 3 to Python 2-compatible code; if these features are important and you need to support multiple versions of Python, this may be of use to you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CPython 2.x's implementation of &lt;code&gt;xrange&lt;/code&gt; does not implement &lt;code&gt;__contains__&lt;/code&gt;, so the expression &lt;code&gt;1000 in xrange(1001)&lt;/code&gt; actually has to scan all 1000 elements in the &lt;code&gt;xrange&lt;/code&gt; before it can determine if 1000 is a member of the sequence. This was corrected in CPython 3, but very long lists in CPython 2.x will have &lt;code&gt;__contains__&lt;/code&gt; performance characteristics more similar to &lt;code&gt;list&lt;/code&gt;s than to 2.7 &lt;code&gt;xrange&lt;/code&gt;s. This implementation uses a constant-time algorithm to compute membership, so it may actually be faster than the CPython 2.x implementation for very large sequences.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content><category term="python"></category><category term="internals"></category></entry><entry><title>The exec Statement and A Python Mystery</title><link href="https://late.am/post/2012/04/30/the-exec-statement-and-a-python-mystery.html" rel="alternate"></link><published>2012-04-30T00:00:00-04:00</published><updated>2012-05-01T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2012-04-30:/post/2012/04/30/the-exec-statement-and-a-python-mystery.html</id><summary type="html">&lt;p&gt;&lt;a href="/post/2012/03/26/exploring-python-code-objects"&gt;A few weeks ago&lt;/a&gt; I examined Python code objects using the &lt;code&gt;dis&lt;/code&gt; module. In that post, I showed several examples of executing code objects created at runtime using the &lt;code&gt;exec&lt;/code&gt; statement; here we'll explore &lt;code&gt;compile()&lt;/code&gt;'s compliment, &lt;code&gt;exec&lt;/code&gt;, how to invoke it, and some of the quirks of using it.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="/post/2012/03/26/exploring-python-code-objects"&gt;A few weeks ago&lt;/a&gt; I examined Python code objects using the &lt;code&gt;dis&lt;/code&gt; module. In that post, I showed several examples of executing code objects created at runtime using the &lt;code&gt;exec&lt;/code&gt; statement; here we'll explore &lt;code&gt;compile()&lt;/code&gt;'s compliment, &lt;code&gt;exec&lt;/code&gt;, how to invoke it, and some of the quirks of using it.&lt;/p&gt;


&lt;p&gt;&lt;em&gt;Note that, like my post on code objects, the information here is only relevant to Python 2.x, specifically, Python 2.7. Unlike that post, at least one technique presented here will &lt;strong&gt;not&lt;/strong&gt; work in Python 3.x -- in 3.x, &lt;code&gt;exec&lt;/code&gt; is a function, and modifications to locals will not propagate to the calling scope. (Thanks to &lt;a href="http://news.ycombinator.com/user?id=comex"&gt;comex&lt;/a&gt; for pointing this out on &lt;a href="http://news.ycombinator.com/item?id=3912390"&gt;Hacker News&lt;/a&gt;.)&lt;/em&gt;&lt;/p&gt;
&lt;h1&gt;A Refresher: &lt;code&gt;compile()&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Before we dive in to &lt;code&gt;exec&lt;/code&gt;, recall that &lt;code&gt;compile()&lt;/code&gt; converts a string of Python source code into a code object, using the same machinery as the usual Python compilation process. It takes three arguments: the source code to compile, the filename of the source code (for which it is customary to use "&lt;string&gt;" for source code not originating in a file), and the "mode" of the compilation (for which "exec" is the most useful mode, at least for these sorts of experiments).&lt;/p&gt;
&lt;h1&gt;Make It Go&lt;/h1&gt;
&lt;p&gt;By itself, a code object is not terribly useful. Sure, it encapsulates all the information needed to execute some Python code, but it is &lt;a href="http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html"&gt;just a noun&lt;/a&gt;, with no way to run itself.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... print &amp;quot;Hello, world&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="ne"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;code&amp;#39;&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;callable&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This isn't terribly useful. Of course we can use the &lt;a href="http://docs.python.org/library/dis.html"&gt;&lt;code&gt;dis&lt;/code&gt;&lt;/a&gt; module to inspect the bytecode, and its attributes to find out what variables it uses, the source file from which this code object was created, etc, but what we really want is to &lt;em&gt;run&lt;/em&gt; it. Enter the &lt;code&gt;exec&lt;/code&gt; statement:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's more like it!&lt;/p&gt;
&lt;h1&gt;Playing With &lt;code&gt;exec&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Imagine that we have a function which accepts a code object and an argument &lt;code&gt;x&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exec_code_and_return_x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;You might expect this function to return the value of &lt;code&gt;x&lt;/code&gt; without modification -- after all, no code in the function actually manipulates &lt;code&gt;x&lt;/code&gt; in any way. However (depending on the code object), you can get some very unexpected outcomes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_code_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... x = x + 1&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exec_code_and_return_x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_code_object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;But you won't always get the unexpected results you expect:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_code_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... del x&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exec_code_and_return_x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_code_object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So what's going on here?&lt;/p&gt;
&lt;h1&gt;On &lt;code&gt;exec&lt;/code&gt; and Scoping&lt;/h1&gt;
&lt;p&gt;Without any additional instructions, &lt;code&gt;exec&lt;/code&gt; uses the current global and local namespaces to execute your code. This means that (as we saw above), code objects being &lt;code&gt;exec&lt;/code&gt;'d can modify variables in scope when it is &lt;code&gt;exec&lt;/code&gt;'d (as well, obviously, as any globals the code object happens to manipulate).&lt;/p&gt;
&lt;p&gt;You can also customize (to a certain extent) the scope given to the code object by using the &lt;code&gt;in&lt;/code&gt; "arguments" to &lt;code&gt;exec&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_globals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_locals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... x = 1&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;code_globals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code_locals&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;code_locals&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note that builtins, as their name suggests, are always available, even when &lt;code&gt;exec&lt;/code&gt;ing with a empty scopes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_globals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_locals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... print unicode(&amp;quot;Hello, world&amp;quot;)&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;code_object&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;code_globals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code_locals&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_globals&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;__builtins__&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Solving Our Python Mystery&lt;/h1&gt;
&lt;p&gt;Returning to our mystery example from before:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_code_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... del x&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exec_code_and_return_x&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_code_object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Why can't the code object delete the local variable &lt;code&gt;x&lt;/code&gt;? Specifically, why is the above code different from this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete_local_then_return_it&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;delete_local_then_return_it&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;delete_local_then_return_it&lt;/span&gt;
&lt;span class="ne"&gt;UnboundLocalError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="n"&gt;variable&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;referenced&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="n"&gt;assignment&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;What we're seeing here is the result of a Python performance optimization: rather than using a dictionary (or some other mapping object) for function locals, and Python bytecode instructions reference positions within this array. As a consequence of this optimization, dynamic code (such as that returned by &lt;code&gt;compile()&lt;/code&gt;, or invocations of &lt;code&gt;exec&lt;/code&gt; directly on a string) cannot modify the size of the locals array, since it is fixed at compile time.&lt;/p&gt;
&lt;p&gt;Moreover, the implementation of &lt;code&gt;exec&lt;/code&gt;, that is, of the &lt;code&gt;EXEC_STMT&lt;/code&gt; opcode, arranges for a new Python stack frame to be pushed, which means that it has its own array of locals (those used by the code object being &lt;code&gt;exec&lt;/code&gt;'d):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;inspect&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_stack_depth&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentframe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="c1"&gt;# begin depth at -1 to account for the extra&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="c1"&gt;# frame created when calling get_stack-depth&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_back&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_stack_depths&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;stack depth in function&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_stack_depth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;...     print &amp;quot;stack depth in exec&amp;quot;, get_stack_depth()&lt;/span&gt;
&lt;span class="s2"&gt;...     &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show_stack_depths&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Code objects can, of course, modify the local variables on their own stack frame:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_code_object_locals&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;code_globals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;code_locals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;del x&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;code_globals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code_locals&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;code_locals&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;show_code_object_locals&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Warnings and Gotchas&lt;/h1&gt;
&lt;p&gt;Using &lt;code&gt;exec&lt;/code&gt; to run code dynamically at runtime is fun, but it is not without its costs.&lt;/p&gt;
&lt;p&gt;First, I would be remiss if I did not &lt;em&gt;strongly urge you never to use &lt;code&gt;exec&lt;/code&gt;&lt;/em&gt;; Python provides no way (and, in fact, there is no general way) to ensure that the code you're attempting to execute will do no harm to your system. Think of this like Python's version of &lt;a href="http://en.wikipedia.org/wiki/Cross-site_scripting"&gt;cross-site scripting attacks&lt;/a&gt;. If you are ever executing untrusted code submitted by end-users of your system, you're doing it wrong.&lt;/p&gt;
&lt;p&gt;Secondly, executing code in this fashion is significantly slower than simply calling regular Python code, as the latter code path is heavily optimized in the interpreter, whereas calling dynamic code is not.&lt;/p&gt;
&lt;p&gt;Finally, if you do need to use &lt;code&gt;exec&lt;/code&gt;, you may find that it is more convenient to pass a string or file object as the first argument -- however, beware that this will incur the cost of compiling the source code each time the &lt;code&gt;exec&lt;/code&gt; statement is encountered. If you are repeatedly &lt;code&gt;exec&lt;/code&gt;ing the same piece of code, you will see significant performance improvement by first &lt;code&gt;compile()&lt;/code&gt;ing and saving the resulting code object for re-use.&lt;/p&gt;</content><category term="python"></category><category term="tinkering"></category></entry><entry><title>A Python "Cast Constructor"</title><link href="https://late.am/post/2012/04/05/a-python-cast-constructor.html" rel="alternate"></link><published>2012-04-05T00:00:00-04:00</published><updated>2012-04-05T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2012-04-05:/post/2012/04/05/a-python-cast-constructor.html</id><summary type="html">&lt;p&gt;Very occasionally, I write code where I'm given an object of some class (usually from a library call), but I wish to use additional methods on that class as though they had been defined there. In some languages (Objective-C shines in this regard with  &lt;a href="http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html"&gt;categories&lt;/a&gt;), you can do this very naturally. In Python, most people probably resort to monkey patching to accomplish this.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Very occasionally, I write code where I'm given an object of some class (usually from a library call), but I wish to use additional methods on that class as though they had been defined there. In some languages (Objective-C shines in this regard with  &lt;a href="http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html"&gt;categories&lt;/a&gt;), you can do this very naturally. In Python, most people probably resort to monkey patching to accomplish this.&lt;/p&gt;


&lt;h1&gt;This is Probably An Anti-Pattern&lt;/h1&gt;
&lt;p&gt;I want to preface this with an admission that I have no idea if this will generally work (please comment if you know of a reason why it won't!), and that I suspect that this is a horrible idea. And yet, here we go:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;copy&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;base foo&amp;#39;&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;base bar&amp;#39;&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__class__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Child&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;child_init&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;child bar&amp;#39;&lt;/span&gt;


&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_init&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;base foo&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;base bar&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__class__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;

&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_init&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;child_init&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;base foo&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;child bar&amp;#39;&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__class__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Child&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="vm"&gt;__class__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Have I made some glaring mistake? Is there something obviously wrong with this, other than the abuse of the language inherent in trying to do such a thing?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; Thanks to &lt;a href="http://emptysquare.net/blog/"&gt;Jesse Davis&lt;/a&gt; for the suggestion to use &lt;code&gt;copy.copy&lt;/code&gt;. Also see &lt;a href="http://stackoverflow.com/questions/10030412/how-to-convert-inherit-parent-to-child-class"&gt;the Stackoverflow question&lt;/a&gt; which inspired this post.&lt;/p&gt;</content><category term="python"></category><category term="anti-pattern"></category></entry><entry><title>Exploring Python Code Objects</title><link href="https://late.am/post/2012/03/26/exploring-python-code-objects.html" rel="alternate"></link><published>2012-03-26T00:00:00-04:00</published><updated>2012-03-31T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2012-03-26:/post/2012/03/26/exploring-python-code-objects.html</id><summary type="html">&lt;p&gt;Inspired by &lt;a href="http://www.dabeaz.com/"&gt;David Beazley&lt;/a&gt;'s &lt;a href="http://pyvideo.org/video/659/keynote-david-beazley"&gt;Keynote&lt;/a&gt; at &lt;a href="https://us.pycon.org/2012/"&gt;PyCon&lt;/a&gt;, I've been digging around in code objects in Python lately. I don't have a particular axe to grind, nor some particular task to solve (yet?), so consider this post just some notes and ramblings that might be of interest (and my apologies if not).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer:&lt;/em&gt; This post is about CPython version 2.7, though much of it is also likely true for other CPython versions (including 3.x). I make no claims to its accuracy or applicability to PyPy, Jython, IronPython, etc.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Inspired by &lt;a href="http://www.dabeaz.com/"&gt;David Beazley&lt;/a&gt;'s &lt;a href="http://pyvideo.org/video/659/keynote-david-beazley"&gt;Keynote&lt;/a&gt; at &lt;a href="https://us.pycon.org/2012/"&gt;PyCon&lt;/a&gt;, I've been digging around in code objects in Python lately. I don't have a particular axe to grind, nor some particular task to solve (yet?), so consider this post just some notes and ramblings that might be of interest (and my apologies if not).&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer:&lt;/em&gt; This post is about CPython version 2.7, though much of it is also likely true for other CPython versions (including 3.x). I make no claims to its accuracy or applicability to PyPy, Jython, IronPython, etc.&lt;/p&gt;


&lt;h1&gt;Step 0: What?&lt;/h1&gt;
&lt;p&gt;So first of all, what is a code object? Many people (particularly Python &lt;a href="http://www.flickr.com/photos/mjstewart/3024478976/"&gt;haters&lt;/a&gt;) claim that Python is an interpreted language, but all your Python code is actually compiled before it is ever executed. This goes even for code you write interactively in the Python shell. CPython implements a virtual machine that executes a stack-based bytecode. At runtime, executable things (functions, methods, modules, class bodies, lambdas, statements, expressions, etc) are all executed as bytecode by the Python virtual machine.&lt;/p&gt;
&lt;p&gt;Code objects, then, are Python objects which represent some piece of bytecode, along with all that it needs to execute: a declaration of the expected argument types and counts, a list (not dictionary! more about which later) of locals, information about the source code from which the bytecode was generated (for debugging and printing stack traces), etc -- oh, and also (perhaps obviously), the bytecode itself, as a &lt;code&gt;str&lt;/code&gt; (or, in Python3, &lt;code&gt;bytes&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Though code objects represent some piece of executable code, they are not, by themselves, directly callable. To execute a code object, you must use the &lt;code&gt;exec&lt;/code&gt; keyword or &lt;code&gt;eval()&lt;/code&gt; function.&lt;/p&gt;
&lt;h1&gt;Step 1: Make some Code&lt;/h1&gt;
&lt;p&gt;Most of the time, you won't encounter code objects in ordinary Python programming. When you do, there's a very good chance that they were created and are managed for you by Python, without any special attention. In some cases, you might want to create code objects yourself, like in this post where we'll be experimenting with them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... print &amp;quot;Hello, world&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x1054c74b0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;string&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Woohoo, your first code object!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first argument to &lt;a href="http://docs.python.org/library/functions.html#compile"&gt;&lt;code&gt;compile()&lt;/code&gt;&lt;/a&gt; is the string of Python code to be compiled, which should be obvious. The second defines the "filename" of the piece of code (here, as is conventional, we use &lt;code&gt;&amp;lt;string&amp;gt;&lt;/code&gt; to indicate code attained from the interactive shell). The third is the type of compilation, which most often will be &lt;code&gt;exec&lt;/code&gt; as you see here. The other choices for mode are &lt;code&gt;eval&lt;/code&gt;, which is used for strings containing only a single expression, or &lt;code&gt;single&lt;/code&gt;, in which the generated code object is expected to contain a single statement, whose return value is printed if it is not &lt;code&gt;None&lt;/code&gt; (like in the interactive shell).&lt;/p&gt;
&lt;p&gt;When using &lt;code&gt;eval&lt;/code&gt; mode, if the code contains statements (as our example above does, it contains a &lt;code&gt;print&lt;/code&gt; statement), compilation will fail with a syntax error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... print &amp;quot;Hello, world&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;eval&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Traceback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="n"&gt;recent&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;string&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Hello, world&amp;quot;&lt;/span&gt;
        &lt;span class="o"&gt;^&lt;/span&gt;
&lt;span class="ne"&gt;SyntaxError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;invalid&lt;/span&gt; &lt;span class="n"&gt;syntax&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;When using &lt;code&gt;single&lt;/code&gt;, only a single statement is processed; multiple statements (or non-statements) will be ignored:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... print &amp;quot;Hello, world&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... print &amp;quot;Goodbye, world&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;single&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;
&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;What happened to my "goodbye"?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For the rest of the post, we'll stick with &lt;code&gt;exec&lt;/code&gt;, which is the type of compilation Python does for you when importing modules.&lt;/p&gt;
&lt;h1&gt;Step 2: Open 'er Up&lt;/h1&gt;
&lt;p&gt;Let's go back to our first example, and have a look inside the code object to see what we have:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... print &amp;quot;Hello, world&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# dunder attributes excluded for readability&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;co_argcount&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_cellvars&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_code&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_consts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_filename&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;co_firstlineno&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_flags&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_freevars&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_lnotab&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;&amp;#39;co_names&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_nlocals&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_stacksize&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;co_varnames&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;These attributes are documented in &lt;a href="http://docs.python.org/library/inspect.html"&gt;the &lt;code&gt;inspect&lt;/code&gt; module&lt;/a&gt;, but I'll highlight a few cool ones here:&lt;/p&gt;
&lt;p&gt;First, we can see where our second argument to &lt;code&gt;compile()&lt;/code&gt; ended up:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_filename&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And, perhaps surprisingly, our code represents an anonymous module (code compiled with &lt;code&gt;exec&lt;/code&gt; mode is always treated as module-level code, though, of course, it can contain function or class definitions, or any other valid Python):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_name&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;&amp;lt;module&amp;gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And, as we expect, a code object representing a Python module (that's effectively what our code string was -- a series of statements at the top-most level, that is, not indented at all) takes no arguments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_argcount&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_varnames&lt;/span&gt;
&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If we were to take a piece of code from a function which does have arguments, we'd see them here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;func_code&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="mh"&gt;0x1054b9830&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;lt;stdin&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;func_code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_varnames&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;y&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;func_code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_argcount&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;If you're curious, you can also see the raw bytecode that will be processed by the Python virtual machine:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_code&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;d&lt;/span&gt;&lt;span class="se"&gt;\x00\x00&lt;/span&gt;&lt;span class="s1"&gt;GHd&lt;/span&gt;&lt;span class="se"&gt;\x01\x00&lt;/span&gt;&lt;span class="s1"&gt;S&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I don't recommend trying to learn to read that directly, there's an easier way (hint: see the next section).&lt;/p&gt;
&lt;p&gt;Finally, we have one constant object within scope, the string "Hello, world", which is printed by our code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_consts&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello, world&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Wait. Where's that &lt;code&gt;None&lt;/code&gt; coming from?&lt;/p&gt;
&lt;h2&gt;A Detour into Code Disassembly&lt;/h2&gt;
&lt;p&gt;We can see exactly what's going on in our code object with the &lt;a href="http://docs.python.org/library/dis.html"&gt;&lt;code&gt;dis&lt;/code&gt;&lt;/a&gt; module, which disassembles code objects into a human readable series of bytecode instructions:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;           &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;LOAD_CONST&lt;/span&gt;           &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Hello, world&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;PRINT_ITEM&lt;/span&gt;
              &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="n"&gt;PRINT_NEWLINE&lt;/span&gt;
              &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;LOAD_CONST&lt;/span&gt;           &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="n"&gt;RETURN_VALUE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Reading disassembled Python code requires a bit of experience, so let me walk you through it. The &lt;code&gt;LOAD_CONST&lt;/code&gt; instruction reads a value from the &lt;code&gt;co_consts&lt;/code&gt; tuple, and pushes it onto the top of the stack. The &lt;code&gt;PRINT_ITEM&lt;/code&gt; instruction pops the top of the stack, and prints the string representation. &lt;code&gt;PRINT_NEWLINE&lt;/code&gt; should be pretty self-explanatory.&lt;/p&gt;
&lt;p&gt;Next we see the mysterious &lt;code&gt;None&lt;/code&gt;. It turns out this is a bit of a quirk of the implementation details of the CPython virtual machine. Since function calls in Python (including "hidden" function calls, like those behind an &lt;code&gt;import&lt;/code&gt; statement) are implemented with function calls in C in the Python virtual machine, modules actually have a return value -- this indicates to the Python virtual machine that execution of the module has completed, and control can be returned to the calling scope (i.e. the module in which the &lt;code&gt;import&lt;/code&gt; statement appeared). I won't embarrass myself by trying to explain this further -- if you are interested, see &lt;a href="http://www.larryhastings.com/"&gt;Larry Hastings&lt;/a&gt;s PyCon presentation &lt;a href="http://pyvideo.org/video/635/stepping-through-cpython"&gt;Stepping through CPython&lt;/a&gt; around 44:22 -- that video covers Python 3.x, but Python 2.7 does the same thing. If you're interested in this sort of implementation detail, then you should definitely watch the entirety of this video, and David Beazley's keynote as well.&lt;/p&gt;
&lt;h1&gt;Step 3: Interesting Internals&lt;/h1&gt;
&lt;p&gt;Many of the features we've looked at are clearly useful for a running Python virtual machine, but what about the human side of the story? What if we want to interactively debug code (using &lt;code&gt;pdb&lt;/code&gt; or a similar tool), or get helpful, readable tracebacks from exceptions?&lt;/p&gt;
&lt;p&gt;It turns out, code objects support this as well. As we've already seen, code objects indicate from which file they were generated, and this will obviously help in looking up source code; they also indicate the line number on which the source code for this code object begins:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_firstlineno&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And the mysterious &lt;code&gt;co_lnotab&lt;/code&gt; attribute. To illustrate its purpose, we'll need a larger code snippet:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... x = 1&lt;/span&gt;
&lt;span class="s2"&gt;... y = 2&lt;/span&gt;
&lt;span class="s2"&gt;... print x + y&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_lnotab&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x06\x01\x06\x01&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Hm, so what are we to make of this? Perhaps the &lt;code&gt;dis&lt;/code&gt; module can help here again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="mi"&gt;2&lt;/span&gt;           &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="n"&gt;LOAD_CONST&lt;/span&gt;           &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;STORE_NAME&lt;/span&gt;           &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="mi"&gt;3&lt;/span&gt;           &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="n"&gt;LOAD_CONST&lt;/span&gt;           &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
              &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="n"&gt;STORE_NAME&lt;/span&gt;           &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="mi"&gt;4&lt;/span&gt;          &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="n"&gt;LOAD_NAME&lt;/span&gt;            &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="n"&gt;LOAD_NAME&lt;/span&gt;            &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="n"&gt;BINARY_ADD&lt;/span&gt;
             &lt;span class="mi"&gt;19&lt;/span&gt; &lt;span class="n"&gt;PRINT_ITEM&lt;/span&gt;
             &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;PRINT_NEWLINE&lt;/span&gt;
             &lt;span class="mi"&gt;21&lt;/span&gt; &lt;span class="n"&gt;LOAD_CONST&lt;/span&gt;           &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="n"&gt;RETURN_VALUE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;At the far left of (some) lines, is the line number of the Python source from which this code object was created (notice that 2 here corresponds to the value of &lt;code&gt;code_obj.co_firstlineno&lt;/code&gt;). The next column is the offset into the code of the bytecode instruction, 0 bytes for the first instruction, 3 bytes for the second, and so on. The third column is the instruction name itself, and the fourth is the argument to the instruction, if any, along with the value of the argument in parentheses.&lt;/p&gt;
&lt;p&gt;Now we can put this together with the &lt;code&gt;co_lnotab&lt;/code&gt; (which stands for "line number table", by the way) to see how Python makes sense of the code objects' relation to their original source code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_lnotab&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x06\x01\x06\x01&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;After a little tinkering and trial and error, I realized that this is a series of pairs of bytes: the first is a length offset into the bytecode (6 bytes, which advances us to the second &lt;code&gt;LOAD_CONST&lt;/code&gt; as seen in our disassembly), followed by a number of source lines of code that the skipped instructions appeared on.&lt;/p&gt;
&lt;p&gt;We can confirm this theory by slightly modifying our source code, recompiling, and examining the &lt;code&gt;co_lnotab&lt;/code&gt; attribute of the resulting code object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_str2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... x = 1&lt;/span&gt;
&lt;span class="s2"&gt;... &lt;/span&gt;
&lt;span class="s2"&gt;... y = 2&lt;/span&gt;
&lt;span class="s2"&gt;... print x + y&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_str2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_lnotab&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x06\x02\x06\x01&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;We've moved the second assignment down one line, so we see that in the second byte of &lt;code&gt;co_lnotab&lt;/code&gt;, we are incrementing the "current line number" by two instead of by one.&lt;/p&gt;
&lt;p&gt;We can also verify that the bytecode resulting from these two (slightly) different source codes is identical:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_code&lt;/span&gt;
&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Since both the bytecode offset and line number offset at single (unsigned) bytes, one might wonder what happens if you have, say, 257 (or more) blank lines between statements in a Python source file? Let's see:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thousand_blanks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... x = 1&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;thousand_blanks&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="s2"&gt;... y = 2&lt;/span&gt;
&lt;span class="s2"&gt;... print x + y&lt;/span&gt;
&lt;span class="s2"&gt;... &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;string&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;exec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code_obj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_lnotab&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\x06\xff\x00\xff\x00\xff\x00\xec\x06\x01&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Since both the bytecode offsets and line offsets are, well, offsets, having large empty spaces just means that some of the interleaved offsets are 0-length offsets. Here we have a 6-byte offset into bytecode, followed by a 255-line offset into the source code, then a 0-byte offset into the bytecode, another 255 lines of source, another 0 bytes into the bytecode, yet another 255 lines of source, one more 0-byte offset into bytecode, and a final 236 lines of offset into the source code (then the usual, expected 6 bytes of bytecode and 1 line of source code for the final &lt;code&gt;print&lt;/code&gt; statement). Neat!&lt;/p&gt;
&lt;h1&gt;Wrapping Up&lt;/h1&gt;
&lt;p&gt;I started by apologizing for the rambling nature of this post, but I hope it's been interesting. Stay tuned for an examination of the nature of &lt;code&gt;exec&lt;/code&gt; and its usage in &lt;a href="http://late.am/post/2011/11/27/keystone-a-simple-python-web-framework"&gt;Keystone&lt;/a&gt; in the near future.&lt;/p&gt;</content><category term="python"></category><category term="internals"></category><category term="tinkering"></category></entry><entry><title>Time to Answer @PythonQuestions</title><link href="https://late.am/post/2012/01/04/time-to-answer-pythonquestions.html" rel="alternate"></link><published>2012-01-04T00:00:00-05:00</published><updated>2012-01-05T00:00:00-05:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2012-01-04:/post/2012/01/04/time-to-answer-pythonquestions.html</id><summary type="html">&lt;p&gt;What's your new year's resolution? One of mine is to be more active in helping and mentoring new Pythonistas, which is why I wrote &lt;a href="http://twitter.com/#!/PythonQuestions"&gt;the twitter bot behind @PythonQuestions&lt;/a&gt;. Inspired by the &lt;a href="https://twitter.com/#!/MongoQuestion"&gt;@MongoQuestion&lt;/a&gt; twitter bot, @PythonQuestions tweets new &lt;a href="http://www.stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; questions tagged "Python".&lt;/p&gt;
</summary><content type="html">&lt;p&gt;What's your new year's resolution? One of mine is to be more active in helping and mentoring new Pythonistas, which is why I wrote &lt;a href="http://twitter.com/#!/PythonQuestions"&gt;the twitter bot behind @PythonQuestions&lt;/a&gt;. Inspired by the &lt;a href="https://twitter.com/#!/MongoQuestion"&gt;@MongoQuestion&lt;/a&gt; twitter bot, @PythonQuestions tweets new &lt;a href="http://www.stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; questions tagged "Python".&lt;/p&gt;


&lt;h1&gt;How it Works&lt;/h1&gt;
&lt;p&gt;@PythonQuestions is actually a (relatively) generic "tweet questions from &lt;a href="http://www.stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt;" bot, composed of three components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The Stack Overflow polling script, which is designed to be run from a cron job (or another means of periodic scheduling)&lt;/li&gt;
&lt;li&gt;The database of questions (&lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; of course), which for now is used only to remember which questions have already been tweeted.&lt;/li&gt;
&lt;li&gt;The Twitter update script, also designed to be run from a cron job.&lt;/li&gt;
&lt;li&gt;(There's also a bare-bones &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; web application which serves redirects for the &lt;a href="http://pyq.io/"&gt;pyq.io&lt;/a&gt; link-shortening domain. The web app currently redirects requests to http://pyq.io/ to the Stack Overflow "Questions tagged Python" page.)&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Polling Stack Overflow&lt;/h1&gt;
&lt;p&gt;Stack Overflow doesn't (to my knowledge) have a Push API, so @PythonQuestions polls the &lt;code&gt;/questions&lt;/code&gt; API periodically (right now, every 5 minutes). It queries using &lt;code&gt;fromdate&lt;/code&gt; sorted by &lt;code&gt;creation&lt;/code&gt; to find recent questions. For @PythonQuestions, it filters questions to only those containing the "python" tag, though this is configurable.&lt;/p&gt;
&lt;p&gt;A few details of these questions are then &lt;a href="http://www.mongodb.org/display/DOCS/Updating#Updating-UpsertswithModifiers"&gt;upserted&lt;/a&gt; into MongoDB using the Stack Overflow question ID as &lt;code&gt;_id&lt;/code&gt;, which ensures that the database only holds one copy of any given question, even if it appears several times while polling.&lt;/p&gt;
&lt;h1&gt;Tweeting About It&lt;/h1&gt;
&lt;p&gt;The twitter script consults the questions database periodically to find questions which have not yet been tweeted about, then uses the &lt;a href="http://flask-tweepy.readthedocs.org/"&gt;Flask-Tweepy&lt;/a&gt; extension (which adapts the &lt;a href="http://packages.python.org/tweepy/html/"&gt;Tweepy&lt;/a&gt; module for use with Flask) to update the status of the @PythonQuestions Twitter user.&lt;/p&gt;
&lt;p&gt;Tweepy makes sending Tweets fairly painless, but Twitter itself sure doesn't. In a future blog post, I'll cover the steps needed to set up an application account, configure it properly, configure your Tweepy appropriately, and get up and running. I've done this once or twice before, so I'm reasonably familiar with the steps, but they're not well or obviously documented on Twitter's Developer site.&lt;/p&gt;
&lt;h1&gt;Putting it Together&lt;/h1&gt;
&lt;p&gt;Using a web application framework for what is essentially a series of scripts may seem an odd choice. Aside from the fact that I hope to expand the scope of the web application, using Flask and its extensions made development painless--the whole application was a matter of a few hours to write, test, and launch. I'm using &lt;a href="http://flask-pymongo.readthedocs.org/"&gt;Flask-PyMongo&lt;/a&gt;, Flask-Tweepy (as previously mentioned), and &lt;a href="http://packages.python.org/Flask-Script/"&gt;Flask-Script&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Flask-Script loads the Flask application object (and thus the configuration), and offers a few decorators to declare settings for &lt;a href="http://docs.python.org/library/argparse.html"&gt;&lt;code&gt;argparse&lt;/code&gt;&lt;/a&gt;. Writing a command-line script then becomes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pythonquestions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;flask.ext.script&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Manager&lt;/span&gt;
&lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@manager.command&lt;/span&gt;
&lt;span class="nd"&gt;@manager.option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;--since&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stackoverflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;1d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# implement the script here&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Critically, this setup lets me share one set of configuration (API keys, usernames, database configuration, etc) for both the cron scripts and the web application.&lt;/p&gt;
&lt;h1&gt;Lend a Hand&lt;/h1&gt;
&lt;p&gt;In his &lt;a href="http://jessenoller.com/2011/12/30/2011-in-review-the-python-portion/"&gt;2011 in Review: The Python Portion&lt;/a&gt;, Jesse Noller called on us to be welcoming to new or inexperienced users, and to mentor them to a point where they can become contributors to, rather than passive members of, the Python community. In some small way, I hope @PythonQuestions can be a part of that.&lt;/p&gt;
&lt;p&gt;Here's how I think that can happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/#!/PythonQuestions"&gt;Follow @PythonQuestions&lt;/a&gt; on Twitter, and start answering questions. Stack Overflow's game mechanics will give you a jolt of dopamine that's the perfect remedy for caffeine wearing off in the afternoon.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dcrosta/python-questions"&gt;Fork the python-questions project on GitHub&lt;/a&gt; and help make it better. I'm especially interested in ideas about what could make the web app portion of it more useful than simply a list of questions (which already exists in better and more useful form at Stack Overflow), or in integrations with other Q&amp;amp;A services or sites frequented by Pythonistas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more broadly, echoing Jesse's thoughts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Be active, constructive, and engaged with less-experienced Pythonistas in your community and online. The goal is to teach, not just to answer specific questions, and certainly not to ridicule or insult (even unintentionally) a novice programmer. Take the time to link to documentation, or explain detail that may not be obvious at first blush.&lt;/li&gt;
&lt;/ul&gt;</content><category term="python"></category><category term="mongodb"></category><category term="flask"></category></entry><entry><title>Hitchhiker's Guide to Python</title><link href="https://late.am/post/2011/12/29/hitchhikers-guide-to-python.html" rel="alternate"></link><published>2011-12-29T00:00:00-05:00</published><updated>2011-12-29T00:00:00-05:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-12-29:/post/2011/12/29/hitchhikers-guide-to-python.html</id><summary type="html">&lt;p&gt;I first heard about &lt;a href="http://docs.python-guide.org/"&gt;The Hitchhiker's Guide to Python&lt;/a&gt; at &lt;a href="http://py.codeconf.com/"&gt;PyCodeConf&lt;/a&gt; a few months ago. It's a fantastic idea: open source, community-driven documentation on how to do Python right: everything from how to learn Python, to how to write idiomatic code, to how to distribute your projects, to surveys of best-of-breed open source projects and libraries you can build projects and applications on top of. Many many thanks to &lt;a href="http://kennethreitz.com/"&gt;Kenneth Reitz&lt;/a&gt; for creating and maintaining the project, which is &lt;a href="https://github.com/kennethreitz/python-guide"&gt;hosted at GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At this time, the Hitchhiker's guide is a little rough around the edges: many sections are only outlined, and need content written; other sections may not even exist yet. We can safely consider it a first draft, or, if you prefer, an alpha.&lt;/p&gt;
&lt;p&gt;This sort of undertaking is effectively impossible for one person to maintain--one person can't possibly know of every project, library, and idiom. Moreover, it's unfair as a user of the Guide to demand that one person must do all the work.&lt;/p&gt;
&lt;p&gt;Therefore, a call to action: in order to make the Python community great, everyone should fork the Hitchhiker's Guide today, find (or add) a section of interest, and submit a pull request. If you're lucky, Kenneth will give you a &lt;a href="https://github.com/kennethreitz/python-guide/pull/26#issuecomment-3256186"&gt;sparkly cake&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And a pledge: time allowing (for, sadly, contributing to Python documentation is not my day job), I will make one contribution to the Hitchhiker's Guide per week until it reaches completion, or until there's nothing left to which I can conscientiously contribute (I won't attempt to document things which I know too little about).&lt;/p&gt;
&lt;p&gt;Finally, since this is a community effort, I want to give a shout-out to all those who've contributed to the Guide so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://kennethreitz.com/"&gt;Kenneth Reitz&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/kennethreitz"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/kennethreitz"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://clipper.ship.edu/~aw9994/"&gt;Aaron Weinberger&lt;/a&gt; (follow on &lt;a href="https://github.com/aaronw"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.kamilkisiel.net/"&gt;Kamil Kisiel&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/kisielk"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/kisielk"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.micrypt.com/"&gt;Seyi Ogunyemi&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/micrypt"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/micrypt"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexgaynor.net"&gt;Alex Gaynor&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/alex_gaynor"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/alex"&gt;Github&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.nuclearrooster.com/"&gt;nstielau&lt;/a&gt; (follow on &lt;a href="https://github.com/nstielau"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://adam.brenecki.id.au/"&gt;Adam Brenecki&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/adambrenecki"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/adambrenecki"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://google.com/profiles/madhav"&gt;Nanda Kishore&lt;/a&gt; (follow on &lt;a href="https://github.com/madhav"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dstufft.com/"&gt;Donald Stufft&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/dstufft"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/dstufft"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.epicserve.com/"&gt;Brent O'Connor&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/epicserve"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/epicserve"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://daltonmatos.com/"&gt;Dalton Barreto&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/daltonmatos"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/daltonmatos"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://unwiredcouch.com/"&gt;Daniel Schauenberg&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/mrtazz"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/mrtazz"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to everyone who's helping to make Python a better place!&lt;/p&gt;
&lt;p&gt;(To the folks above, if you'd like me to add or correct any of the contact information, please leave a comment.)&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I first heard about &lt;a href="http://docs.python-guide.org/"&gt;The Hitchhiker's Guide to Python&lt;/a&gt; at &lt;a href="http://py.codeconf.com/"&gt;PyCodeConf&lt;/a&gt; a few months ago. It's a fantastic idea: open source, community-driven documentation on how to do Python right: everything from how to learn Python, to how to write idiomatic code, to how to distribute your projects, to surveys of best-of-breed open source projects and libraries you can build projects and applications on top of. Many many thanks to &lt;a href="http://kennethreitz.com/"&gt;Kenneth Reitz&lt;/a&gt; for creating and maintaining the project, which is &lt;a href="https://github.com/kennethreitz/python-guide"&gt;hosted at GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At this time, the Hitchhiker's guide is a little rough around the edges: many sections are only outlined, and need content written; other sections may not even exist yet. We can safely consider it a first draft, or, if you prefer, an alpha.&lt;/p&gt;
&lt;p&gt;This sort of undertaking is effectively impossible for one person to maintain--one person can't possibly know of every project, library, and idiom. Moreover, it's unfair as a user of the Guide to demand that one person must do all the work.&lt;/p&gt;
&lt;p&gt;Therefore, a call to action: in order to make the Python community great, everyone should fork the Hitchhiker's Guide today, find (or add) a section of interest, and submit a pull request. If you're lucky, Kenneth will give you a &lt;a href="https://github.com/kennethreitz/python-guide/pull/26#issuecomment-3256186"&gt;sparkly cake&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And a pledge: time allowing (for, sadly, contributing to Python documentation is not my day job), I will make one contribution to the Hitchhiker's Guide per week until it reaches completion, or until there's nothing left to which I can conscientiously contribute (I won't attempt to document things which I know too little about).&lt;/p&gt;
&lt;p&gt;Finally, since this is a community effort, I want to give a shout-out to all those who've contributed to the Guide so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://kennethreitz.com/"&gt;Kenneth Reitz&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/kennethreitz"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/kennethreitz"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://clipper.ship.edu/~aw9994/"&gt;Aaron Weinberger&lt;/a&gt; (follow on &lt;a href="https://github.com/aaronw"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.kamilkisiel.net/"&gt;Kamil Kisiel&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/kisielk"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/kisielk"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.micrypt.com/"&gt;Seyi Ogunyemi&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/micrypt"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/micrypt"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://alexgaynor.net"&gt;Alex Gaynor&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/alex_gaynor"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/alex"&gt;Github&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dev.nuclearrooster.com/"&gt;nstielau&lt;/a&gt; (follow on &lt;a href="https://github.com/nstielau"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://adam.brenecki.id.au/"&gt;Adam Brenecki&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/adambrenecki"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/adambrenecki"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://google.com/profiles/madhav"&gt;Nanda Kishore&lt;/a&gt; (follow on &lt;a href="https://github.com/madhav"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dstufft.com/"&gt;Donald Stufft&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/dstufft"&gt;Twitter&lt;/a&gt; and &lt;a href="https://github.com/dstufft"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.epicserve.com/"&gt;Brent O'Connor&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/epicserve"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/epicserve"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://daltonmatos.com/"&gt;Dalton Barreto&lt;/a&gt; (follow on &lt;a href="https://twitter.com/#!/daltonmatos"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/daltonmatos"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://unwiredcouch.com/"&gt;Daniel Schauenberg&lt;/a&gt; (follow on &lt;a href="http://twitter.com/#!/mrtazz"&gt;Twitter&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/mrtazz"&gt;GitHub&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to everyone who's helping to make Python a better place!&lt;/p&gt;
&lt;p&gt;(To the folks above, if you'd like me to add or correct any of the contact information, please leave a comment.)&lt;/p&gt;
</content><category term="python"></category><category term="community"></category></entry><entry><title>Customize Virtualenvwrapper for Fun and Profit</title><link href="https://late.am/post/2011/12/22/customize-virtualenvwrapper-for-fun-and-profit.html" rel="alternate"></link><published>2011-12-22T00:00:00-05:00</published><updated>2011-12-23T00:00:00-05:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-12-22:/post/2011/12/22/customize-virtualenvwrapper-for-fun-and-profit.html</id><summary type="html">&lt;p&gt;&lt;a href="http://tech.blog.aknin.name/"&gt;Yaniv Aknin&lt;/a&gt; (whose blog I found through &lt;a href="http://planet.python.org/"&gt;Planet Python&lt;/a&gt;) recently &lt;a href="http://tech.blog.aknin.name/2010/10/14/zsh-and-virtualenv/"&gt;blogged&lt;/a&gt; about his ZSH and &lt;a href="http://www.virtualenv.org/"&gt;virtualenv&lt;/a&gt; setup. As I was reading it, I recognized that he's solving some of the same problems I only recently solved myself (though through a very different approach than he took), so I figured I would share it, too.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;a href="http://tech.blog.aknin.name/"&gt;Yaniv Aknin&lt;/a&gt; (whose blog I found through &lt;a href="http://planet.python.org/"&gt;Planet Python&lt;/a&gt;) recently &lt;a href="http://tech.blog.aknin.name/2010/10/14/zsh-and-virtualenv/"&gt;blogged&lt;/a&gt; about his ZSH and &lt;a href="http://www.virtualenv.org/"&gt;virtualenv&lt;/a&gt; setup. As I was reading it, I recognized that he's solving some of the same problems I only recently solved myself (though through a very different approach than he took), so I figured I would share it, too.&lt;/p&gt;


&lt;h1&gt;What is &lt;code&gt;virtualenv&lt;/code&gt;?&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;virtualenv&lt;/code&gt; is, in my opinion, really the only way to do sane Python development. As its name implies, &lt;code&gt;virtualenv&lt;/code&gt; creates a virtual Python environment, and in so doing solves a number of problems quite elegantly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You are working on more than one project, each of which requires some of the same dependencies, but at different versions. You aren't using &lt;a href="http://packages.python.org/distribute/"&gt;distribute&lt;/a&gt;'s &lt;code&gt;pkg_resources&lt;/code&gt;, so you can't know that the right version will be used by the right project (nor even which version will be used when imported).&lt;/li&gt;
&lt;li&gt;Alternately, you are working on more than one project, each of which requires &lt;em&gt;different&lt;/em&gt; dependencies, but your dependencies are poorly-behaved and trample one another's namespaces.&lt;/li&gt;
&lt;li&gt;You want to be sure that your development and deployment environments are as identical as possible with regards to package makeup.&lt;/li&gt;
&lt;li&gt;You have grown tired of typing &lt;code&gt;sudo pip install ...&lt;/code&gt; to install packages.&lt;/li&gt;
&lt;li&gt;You have grown tired of errors about &lt;code&gt;PYTHON_EGG_CACHE&lt;/code&gt; when using zipped eggs (though I would be remiss if I didn't note: don't use zipped eggs, they are a stupid and awful idea, which could be the subject of a different blog post)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;virtualenv&lt;/code&gt; solves all of these in one fell swoop by creating what is essentially a clone of your standard python installation into a new location, optionally including any system-installed site-packages, configures a few environment variables, and sets everything to be owned and usable by your unprivileged user.&lt;/p&gt;
&lt;p&gt;After creating the virtual environment, you then activate it by sourcing the &lt;code&gt;bin/activate&lt;/code&gt; script inside the virtual environment. Now when you type &lt;code&gt;python&lt;/code&gt; at your command prompt, you are activating the &lt;code&gt;virtualenv&lt;/code&gt;'s Python executable rather than the system executable, and all Python scripts you run (importantly, &lt;code&gt;pip&lt;/code&gt;) will use this executable as well. Your virtual environment has its own &lt;code&gt;site-packages&lt;/code&gt; directory, and package installs done while the environment is activated will install into the virtual environment as well.&lt;/p&gt;
&lt;p&gt;When you're done, simply type &lt;code&gt;deactivate&lt;/code&gt; (or close your shell) to undo everything that the &lt;code&gt;activate&lt;/code&gt; script did.&lt;/p&gt;
&lt;h1&gt;And what about &lt;code&gt;virtualenvwrapper&lt;/code&gt;?&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;virtualenv&lt;/code&gt; isn't without its deficiencies, though. It's up to you to manage your own virtual environments and to remember where each one's &lt;code&gt;activate&lt;/code&gt; script lies.&lt;/p&gt;
&lt;p&gt;Many people adopt the convention that you create a directory named &lt;code&gt;env&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt; containing the virtual environment within the project root folder of the project for which the environment is meant. However, this clutters your checkout root, and you might mistakenly check your virtual environment in to source control.&lt;/p&gt;
&lt;p&gt;Alternately you can have a centralized location for all your environments, but then you have to remember two paths for each project, rather than one, and you have to remember it each time you want to open a new shell or terminal tab for a given project.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.doughellmann.com/projects/virtualenvwrapper/"&gt;&lt;code&gt;virtualenvwrapper&lt;/code&gt;&lt;/a&gt; is a well-thought-out set of shell function wrappers for &lt;code&gt;virtualenv&lt;/code&gt; which make life using it much better.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;virtualenvwrapper&lt;/code&gt; stores all your environments in one place (by default in &lt;code&gt;~/.virtualenvs&lt;/code&gt;, or wherever the &lt;code&gt;$WORKON_HOME&lt;/code&gt; environment variable points), but alleviates the pain of that approach by providing a suite of tools to manipulate and interrogate the virtual environments you have.&lt;/p&gt;
&lt;p&gt;There is &lt;code&gt;mkvirtualenv&lt;/code&gt; which supersedes the &lt;code&gt;virtualenv&lt;/code&gt; command, and its new counterpart &lt;code&gt;rmvirtualenv&lt;/code&gt;; &lt;code&gt;lsvirtualenv&lt;/code&gt; which lists all the &lt;code&gt;virtualenvwrapper&lt;/code&gt;-managed environments; &lt;code&gt;cdvirtualenv&lt;/code&gt; and &lt;code&gt;cdsitepackages&lt;/code&gt; which change directory to the virtual environment's root or site-packages directories, respectively; and my favorite, &lt;code&gt;workon&lt;/code&gt;, which activates an environment by name.&lt;/p&gt;
&lt;h1&gt;But I want more!&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;virtualenvwrapper&lt;/code&gt; also has a full suite of hook scripts (both "global" hooks that are used regardless of which environment you are working on, and environment-specific versions) to customize aspects of its behavior. I'll mention two here which I've found incredibly useful:&lt;/p&gt;
&lt;p&gt;First is the &lt;code&gt;postmkvirtualenv&lt;/code&gt; hook, which runs after any invocation of &lt;code&gt;mkvirtualenv&lt;/code&gt;. I have mine set up to create a directory named after the virtual envrionment as a subdirectory of the current working directory, if it doesn't already exist, and then &lt;code&gt;cd&lt;/code&gt; into it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# ~/.virtualenvs/postmkvirtualenv&lt;/span&gt;
&lt;span class="nv"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;envname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;basename &lt;span class="nv"&gt;$VIRTUAL_ENV&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="nv"&gt;envdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$parent&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$envname&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; ! -e &lt;span class="nv"&gt;$envdir&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    mkdir -p &lt;span class="nv"&gt;$envdir&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$envdir&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;$VIRTUAL_ENV&lt;/code&gt; variable points to the directory of the newly-created virtual environment, something like &lt;code&gt;~/.virtualenvs/foobar&lt;/code&gt;, and is set because before the &lt;code&gt;postmkvirtualenv&lt;/code&gt; hook is run, &lt;code&gt;virtualenvwrapper&lt;/code&gt; has already activated the new environment for us.&lt;/p&gt;
&lt;p&gt;Secondly, I use the &lt;code&gt;postmkvirtualenv&lt;/code&gt; hook to write a &lt;code&gt;postactivate&lt;/code&gt; hook into the newly created virtual environment, so that on subsequent activations with &lt;code&gt;workon&lt;/code&gt;, I also get this "&lt;code&gt;cd&lt;/code&gt; to the right place" behavior:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# also in ~/.virtualenvs/postmkvirtualenv&lt;/span&gt;
&lt;span class="nv"&gt;postactivate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$VIRTUAL_ENV&lt;/span&gt;/bin/postactivate

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;cd &lt;/span&gt;&lt;span class="nv"&gt;$envdir&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;             &amp;gt;&amp;gt; &lt;span class="nv"&gt;$postactivate&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;With these two hooks configured, I can now forget about all paths, either to the virtual environment itself, or to my project's source root; all I need to remember is the name I used when I created it, and type &lt;code&gt;workon that_name&lt;/code&gt;, and I'll be brought to the right place. And if I happen to forget the name, I've always got &lt;code&gt;lsvirtualenv&lt;/code&gt; to help me remember.&lt;/p&gt;
&lt;p&gt;My &lt;code&gt;virtualenvwrapper&lt;/code&gt; hooks (as well as my ZSH setup, vim configuration, and a few other bits of random customization) are in my &lt;a href="https://github.com/dcrosta/dotfiles"&gt;dotfiles repository&lt;/a&gt; -- feel free to fork it if you think it'll be useful for you!&lt;/p&gt;</content><category term="python"></category></entry><entry><title>Truncating HTML with Python</title><link href="https://late.am/post/2011/12/02/truncating-html-with-python.html" rel="alternate"></link><published>2011-12-02T00:00:00-05:00</published><updated>2012-06-15T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-12-02:/post/2011/12/02/truncating-html-with-python.html</id><summary type="html">&lt;p&gt;On the &lt;a href="http://www.10gen.com/"&gt;10gen&lt;/a&gt; &lt;a href="http://www.10gen.com/events"&gt;events pages&lt;/a&gt; we show a table of sessions for our &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; Day conferences, some of which are &lt;a href="http://www.10gen.com/events/mongosv-2011"&gt;quite crowded&lt;/a&gt;. In order to try to keep the table relatively sanely laid out, we truncate the session descriptions to about 150 characters, and add a "Read More" link which expands to show the full description.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;On the &lt;a href="http://www.10gen.com/"&gt;10gen&lt;/a&gt; &lt;a href="http://www.10gen.com/events"&gt;events pages&lt;/a&gt; we show a table of sessions for our &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; Day conferences, some of which are &lt;a href="http://www.10gen.com/events/mongosv-2011"&gt;quite crowded&lt;/a&gt;. In order to try to keep the table relatively sanely laid out, we truncate the session descriptions to about 150 characters, and add a "Read More" link which expands to show the full description.&lt;/p&gt;


&lt;p&gt;Our initial approach did this truncation on the client side, using Javascript, but we found that this was difficult to get right, particularly when the description spanned several paragraphs or other block-level tags. So I wrote a quick &lt;a href="http://docs.python.org/library/htmlparser.html#HTMLParser.HTMLParser"&gt;HTMLParser&lt;/a&gt; sub-class to do the truncation on the server side.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;HTMLParser&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTMLParser&lt;/span&gt;

&lt;span class="n"&gt;whitespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;(\w+)&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HTMLAbbrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTMLParser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxlength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;HTMLParser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxlength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;maxlength&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxlength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# trim trailing whitespace&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="c1"&gt;# close out tags on the stack&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_starttag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;&lt;/span&gt;&lt;span class="si"&gt;%s%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_endtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;/&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;end tag &lt;/span&gt;&lt;span class="si"&gt;%r&lt;/span&gt;&lt;span class="s1"&gt; does not match stack: &lt;/span&gt;&lt;span class="si"&gt;%r&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_startendtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;=&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;lt;&lt;/span&gt;&lt;span class="si"&gt;%s%s&lt;/span&gt;&lt;span class="s1"&gt;/&amp;gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39; &amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;whitespace&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_entityref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;amp;&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_charref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handle_entityref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;em&gt;(You can download the source from &lt;a href="https://gist.github.com/1339994"&gt;this gist&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;HTMLAbbrev&lt;/code&gt; attempts to truncate to a target number of visible (i.e. in the browser) characters --- in fact, it will truncate at the first word break after the target number of characters --- and maintains a tag stack so that the emitted HTML is correctly closed, to prevent breaking layout in the browser. It doesn't count HTML tag contents towards the length, so links and other markup won't be counted towards the total.&lt;/p&gt;
&lt;p&gt;We have a call to &lt;code&gt;HTMLAbbrev&lt;/code&gt; wrapped into a &lt;a href="http://jinja.pocoo.org/"&gt;Jinja&lt;/a&gt; template filter, like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@filter&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;htmlabbrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxlen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HTMLAbbrev&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxlen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Which we then call from our templates:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;abbrev&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;session.description&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nf"&gt;htmlabbrev&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;120&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;ellipsis&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;more clickable&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Read More&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;full&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;session.description&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;less clickable&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hide&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;CSS hides the &lt;code&gt;&amp;lt;div class="full"&amp;gt;&lt;/code&gt; element by default, and javascript toggles between displaying the two.&lt;/p&gt;</content><category term="python"></category><category term="web development"></category></entry><entry><title>Keystone: A Simple Python Web Framework</title><link href="https://late.am/post/2011/11/27/keystone-a-simple-python-web-framework.html" rel="alternate"></link><published>2011-11-27T00:00:00-05:00</published><updated>2011-11-27T00:00:00-05:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-11-27:/post/2011/11/27/keystone-a-simple-python-web-framework.html</id><summary type="html">&lt;p&gt;After a conversation with &lt;a href="http://www.emptysquare.net/blog/"&gt;my friend and co-worker Jesse Davis&lt;/a&gt; last week about Python web frameworks, I had an idea: what if there were a Python web framework that combined some of the simplicity of workflow and deployment of PHP with the readability and embodiment of best practices of Python? This should be a framework targeted towards folks who want to (or need to) learn web development, but don't have the background, interest, or time to learn one of the heavy-weight frameworks (like &lt;a href="http://www.pylonsproject.org/"&gt;Pyramid&lt;/a&gt; or &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt;) or middle-weight frameworks (like &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; or &lt;a href="http://cherrypy.org/"&gt;CherryPy&lt;/a&gt;). This framework should let you get started immediately, and let you move smoothly from static sites to dynamic sites, let you learn best practices of both Python and web programming, and should not stand in your way when it comes time to go live or scale up.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;After a conversation with &lt;a href="http://www.emptysquare.net/blog/"&gt;my friend and co-worker Jesse Davis&lt;/a&gt; last week about Python web frameworks, I had an idea: what if there were a Python web framework that combined some of the simplicity of workflow and deployment of PHP with the readability and embodiment of best practices of Python? This should be a framework targeted towards folks who want to (or need to) learn web development, but don't have the background, interest, or time to learn one of the heavy-weight frameworks (like &lt;a href="http://www.pylonsproject.org/"&gt;Pyramid&lt;/a&gt; or &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt;) or middle-weight frameworks (like &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt; or &lt;a href="http://cherrypy.org/"&gt;CherryPy&lt;/a&gt;). This framework should let you get started immediately, and let you move smoothly from static sites to dynamic sites, let you learn best practices of both Python and web programming, and should not stand in your way when it comes time to go live or scale up.&lt;/p&gt;


&lt;p&gt;Well, it turns out something &lt;em&gt;almost&lt;/em&gt; like this already exists, &lt;a href="http://aspen.io/"&gt;Aspen&lt;/a&gt;. Aspen has a number of very nice features: view code and template code combined in one file, but without being fully mixed in like PHP; leveraging the filesystem for URL mapping; and a relatively simple set-it-up-and-try-it-out story. It's got a few things I really don't like, too: the use of ASCII page breaks (&lt;code&gt;^L&lt;/code&gt; characters) to separate Python from templates; lots of configuration; and about 5,300 lines of code.&lt;/p&gt;
&lt;p&gt;So, inspired by what's great about Aspen, and determined not to duplicate what's not, I give you &lt;a href="https://github.com/dcrosta/keystone"&gt;Keystone&lt;/a&gt;!&lt;/p&gt;
&lt;h1&gt;Hang on, another Python web framework?&lt;/h1&gt;
&lt;p&gt;Yes. It's OK. There are a lot of them. Choice is good. Let's move on.&lt;/p&gt;
&lt;h1&gt;Keystone's Main Idea&lt;/h1&gt;
&lt;p&gt;Keystone's views combine Python and (&lt;a href="http://jinja.pocoo.org/"&gt;Jinja&lt;/a&gt;) template code into the same file, which must end with extension &lt;code&gt;.ks&lt;/code&gt;. To avoid the tag-and-code soup that most people start out with in PHP, Keystone separates the code and template with four hyphens. No Python is allowed below the line, and no literal template code above it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Pal&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Friend&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Buddy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="o"&gt;----&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;doctype&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Greetings&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;Keystone&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This does pretty much exactly what you'd expect: on each request, the Python code is executed and randomly assigns one of "Pal", "Friend", or "Buddy" to the name variable; that variable is then injected into the template's rendering context, and the template renders the HTML to be returned to the user.&lt;/p&gt;
&lt;p&gt;You can use all of Python in Keystone views, import standard, third party, or custom modules, etc. All in-scope variables at the end of execution are made available to the template for rendering (i.e. there's something like an implicit &lt;code&gt;return locals()&lt;/code&gt; at the end of the view code).&lt;/p&gt;
&lt;p&gt;Keystone also injects a number of variables into your Python code: &lt;code&gt;request&lt;/code&gt; which contains &lt;code&gt;method&lt;/code&gt;, &lt;code&gt;cookies&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt;, and other helpful attributes; (response) &lt;code&gt;headers&lt;/code&gt; for controlling the HTTP response; &lt;code&gt;set_cookie&lt;/code&gt; and &lt;code&gt;delete_cookie&lt;/code&gt;; and a few others.&lt;/p&gt;
&lt;h1&gt;Mapping URLs&lt;/h1&gt;
&lt;p&gt;The URL of a Keystone view is its path, relative to the application root. In other words, &lt;code&gt;GET /&lt;/code&gt; will execute &lt;code&gt;$APP/index.ks&lt;/code&gt;; &lt;code&gt;GET /foo&lt;/code&gt; will execute &lt;code&gt;$APP/foo.ks&lt;/code&gt;; &lt;code&gt;GET /foo/bar/&lt;/code&gt; will execute &lt;code&gt;$APP/foo/bar/index.ks&lt;/code&gt;; etc.&lt;/p&gt;
&lt;p&gt;Keystone also allows for parameterized paths, by prefixing the name of any directory or &lt;code&gt;.ks&lt;/code&gt; file within the application root with "&lt;code&gt;%&lt;/code&gt;". So &lt;code&gt;GET /account/dcrosta&lt;/code&gt; would execute &lt;code&gt;$APP/account/%username.ks&lt;/code&gt;, and within that view, the variable &lt;code&gt;username&lt;/code&gt; would have the value "dcrosta".&lt;/p&gt;
&lt;h1&gt;Running Keystone&lt;/h1&gt;
&lt;p&gt;Keystone comes with a &lt;code&gt;keystone&lt;/code&gt; command-line script which runs a development web server, including in-browser traceback display and inspection.&lt;/p&gt;
&lt;p&gt;When it comes time to deploy a Keystone application, the &lt;code&gt;keystone&lt;/code&gt; script can generate boilerplate code for you automatically in several of the popular Platform-as-a-Service providers (currently &lt;a href="http://www.heroku.com/"&gt;Heroku&lt;/a&gt;, &lt;a href="https://www.dotcloud.com/"&gt;DotCloud&lt;/a&gt;, or &lt;a href="https://ep.io/"&gt;ep.io&lt;/a&gt;, and possibly more in the future) or a stock WSGI script.&lt;/p&gt;
&lt;h1&gt;Ready to Begin?&lt;/h1&gt;
&lt;p&gt;Check out Keystone's &lt;a href="http://keystone.readthedocs.org/"&gt;documentation for beginners and experts alike&lt;/a&gt; or &lt;a href="https://github.com/dcrosta/keystone"&gt;source repository&lt;/a&gt;, and dive in. I'd love to hear feedback on all of the above, so please leave a comment.&lt;/p&gt;</content><category term="python"></category><category term="web development"></category></entry><entry><title>Ensuring Write-Your-Own-Reads Consistency in MongoDB</title><link href="https://late.am/post/2011/11/18/ensuring-write-your-own-reads-consistency-in-mongodb.html" rel="alternate"></link><published>2011-11-18T00:00:00-05:00</published><updated>2011-11-18T00:00:00-05:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-11-18:/post/2011/11/18/ensuring-write-your-own-reads-consistency-in-mongodb.html</id><summary type="html">&lt;p&gt;In a &lt;a href="http://stackoverflow.com/questions/8081629/mongodb-document-operations-are-atomic-and-isolated-but-are-they-consistent"&gt;question on StackOverflow&lt;/a&gt; a few days ago, a user was asking how to ensure that a document hasn't changed between when a client read the document and wrote to it. If user A reads the document and makes some changes (through a web form, for instance), the change should be accepted if and only if no other user B has updated the document since when user A read the document.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; doesn't support transactions, and even if it did, they wouldn't help in this case. An assumption underlying the question is that the time between any given user's reads and writes is long -- otherwise explicit/pessimistic locking would be the simplest solution -- in which case holding a transaction open on a traditional database server would be prohibitively costly (in terms of resource usage and performance).&lt;/p&gt;
&lt;p&gt;The solution is to leverage MongoDB's atomic update semantics with an optimistic concurrency solution. This comprises four basic steps: 1. read a document; 2. modify the document (i.e. present it to the user in a web form); 3. validate that the document hasn't changed; 4. commit or abandon the user's update. For anyone who's used a source code version control system before, these steps should be familiar (i.e. pull, work locally, commit, and push for git users).&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In a &lt;a href="http://stackoverflow.com/questions/8081629/mongodb-document-operations-are-atomic-and-isolated-but-are-they-consistent"&gt;question on StackOverflow&lt;/a&gt; a few days ago, a user was asking how to ensure that a document hasn't changed between when a client read the document and wrote to it. If user A reads the document and makes some changes (through a web form, for instance), the change should be accepted if and only if no other user B has updated the document since when user A read the document.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; doesn't support transactions, and even if it did, they wouldn't help in this case. An assumption underlying the question is that the time between any given user's reads and writes is long -- otherwise explicit/pessimistic locking would be the simplest solution -- in which case holding a transaction open on a traditional database server would be prohibitively costly (in terms of resource usage and performance).&lt;/p&gt;
&lt;p&gt;The solution is to leverage MongoDB's atomic update semantics with an optimistic concurrency solution. This comprises four basic steps: 1. read a document; 2. modify the document (i.e. present it to the user in a web form); 3. validate that the document hasn't changed; 4. commit or abandon the user's update. For anyone who's used a source code version control system before, these steps should be familiar (i.e. pull, work locally, commit, and push for git users).&lt;/p&gt;


&lt;h1&gt;How to Version Your Documents&lt;/h1&gt;
&lt;p&gt;In order to do steps 1 and 3 efficiently, add a version field to the document. In most cases, this can be an always-increasing number, which MongoDB allows us to easily modify using &lt;code&gt;$inc&lt;/code&gt;. When we create a document for the first time, add a field &lt;code&gt;version&lt;/code&gt; and set its value to 0 (or 1 if you prefer):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(...),&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Ensuring Write-Your-Own-Reads Consistency in MongoDB&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mongodb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;In a question on StackOverflow...&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Next we load the document and display a form to the user, being sure to hold on to the version in a hidden field. Once the form is submitted, we calculate the changes the user made (or simply prepare to overwrite the document with the new values), but we issue our update using the previous version number:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blogposts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_id&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;$set&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;# key-value pairs from the form,&lt;/span&gt;
    &lt;span class="c1"&gt;# excluding the &amp;quot;version&amp;quot; field&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s1"&gt;&amp;#39;$inc&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;version&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;safe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;By using a &lt;code&gt;safe=True&lt;/code&gt; write, PyMongo (and the other MongoDB drivers) will batch a call to &lt;code&gt;getLastError&lt;/code&gt; with our update. The result of &lt;code&gt;getLastError&lt;/code&gt; will indicate how many documents were updated -- in our case, since we are not using a multi-update, we expect that number to be 1 in the happy case (i.e. no one else updated the document in the meantime) or 0 if a conflict was detected (i.e. the version is no longer 0). The &lt;code&gt;getLastError&lt;/code&gt; output will look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;updatedExisting&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;connectionId&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;112&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ok&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;err&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;n&lt;/code&gt; field in the &lt;code&gt;getLastError&lt;/code&gt; output is the one we want to look at -- if it's 1, then the document was updated and will now have a version number of 1; if it's 0, then there was a conflicting update.&lt;/p&gt;
&lt;p&gt;What to do in the case of conflicting updates is up to the application, MongoDB doesn't handle this for you at all (in fact, MongoDB isn't even aware of the fact that you're trying to do optimistic locking at all). A few common solutions are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Show the user the form again, with a message indicating that another user has updated the document, and asking the user to handle the error&lt;/li&gt;
&lt;li&gt;If possible, handle the conflict automatically in your application (this might require having tracked all the values of the previous version of the form, in order to detect which values have changed and what the delta is)&lt;/li&gt;
&lt;li&gt;Throw up your arms, admit that programming is hard, and go shopping.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OK, maybe not the last one :)&lt;/p&gt;</content><category term="mongodb"></category></entry><entry><title>Web A/B Testing with Dabble</title><link href="https://late.am/post/2011/11/04/web-a-b-testing-with-dabble.html" rel="alternate"></link><published>2011-11-04T00:00:00-04:00</published><updated>2011-11-04T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-11-04:/post/2011/11/04/web-a-b-testing-with-dabble.html</id><summary type="html">&lt;p&gt;Thanks to a &lt;a href="http://37signals.com/svn/posts/2977-behind-the-scenes-highrise-marketing-site-ab-testing-part-1"&gt;series&lt;/a&gt; &lt;a href="http://37signals.com/svn/posts/2977-behind-the-scenes-highrise-marketing-site-ab-testing-part-2"&gt;of&lt;/a&gt; &lt;a href="http://37signals.com/svn/posts/2977-behind-the-scenes-highrise-marketing-site-ab-testing-part-3"&gt;recent&lt;/a&gt; &lt;a href="http://37signals.com/svn/posts/3004-ab-testing-tech-note-determining-sample-size"&gt;posts&lt;/a&gt; on the &lt;a href="http://37signals.com/svn/"&gt;SvN blog&lt;/a&gt;, I've been thinking more about my little Python A/B testing framework, &lt;a href="https://github.com/dcrosta/dabble"&gt;Dabble&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I built Dabble to A/B test (sometimes also called "split test") features on &lt;a href="http://www.10gen.com/"&gt;10gen.com&lt;/a&gt;. Following the advice of a blog post I've since lost track of, Dabble configures A/B test parameters entirely in code, follows procedure for independent testing, and generally works without much of a hassle.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Thanks to a &lt;a href="http://37signals.com/svn/posts/2977-behind-the-scenes-highrise-marketing-site-ab-testing-part-1"&gt;series&lt;/a&gt; &lt;a href="http://37signals.com/svn/posts/2977-behind-the-scenes-highrise-marketing-site-ab-testing-part-2"&gt;of&lt;/a&gt; &lt;a href="http://37signals.com/svn/posts/2977-behind-the-scenes-highrise-marketing-site-ab-testing-part-3"&gt;recent&lt;/a&gt; &lt;a href="http://37signals.com/svn/posts/3004-ab-testing-tech-note-determining-sample-size"&gt;posts&lt;/a&gt; on the &lt;a href="http://37signals.com/svn/"&gt;SvN blog&lt;/a&gt;, I've been thinking more about my little Python A/B testing framework, &lt;a href="https://github.com/dcrosta/dabble"&gt;Dabble&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I built Dabble to A/B test (sometimes also called "split test") features on &lt;a href="http://www.10gen.com/"&gt;10gen.com&lt;/a&gt;. Following the advice of a blog post I've since lost track of, Dabble configures A/B test parameters entirely in code, follows procedure for independent testing, and generally works without much of a hassle.&lt;/p&gt;


&lt;h1&gt;Dabble from 10,000 Feet&lt;/h1&gt;
&lt;p&gt;Aside from a little necessary configuration, Dabble is composed of two user-facing classes: &lt;code&gt;ABTest&lt;/code&gt; and &lt;code&gt;ABParameter&lt;/code&gt;. The former represents the thing you're testing -- for instance, the design of a signup page or a call-to-action form; the latter is where you actually implement the variants which make up the tested features.&lt;/p&gt;
&lt;p&gt;Here's an example, using a class-based Python framework:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Signup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/signup&amp;#39;&lt;/span&gt;

    &lt;span class="n"&gt;abest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ABTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;signup_button&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;green&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;show&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;signup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ABParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;signup_button&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;#FF0000&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;#00FF00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# do some stuff&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abtest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;show&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;signup.html, color=self.color)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# validate the submission&lt;/span&gt;
        &lt;span class="c1"&gt;# save the user&amp;#39;s information&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;abtest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;signup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;And in your template:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/signup&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;post&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- form goes here --&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;background-color:&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;color&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That's it.&lt;/p&gt;
&lt;p&gt;Wait, what?&lt;/p&gt;
&lt;p&gt;Yup, that's it. No if/else, no ugly duplication of code across templates. Just one template variable.&lt;/p&gt;
&lt;p&gt;A/B testing is really that simple. Ruby's got &lt;a href="https://github.com/paulmars/seven_minute_abs"&gt;7 Minute Abs&lt;/a&gt;, and now Python's got 30 second Dabbles... or something... I guess I'll have to work on that.&lt;/p&gt;
&lt;h1&gt;Behind the Scenes&lt;/h1&gt;
&lt;p&gt;What magic makes this possible? First, let's take a look at the parent class for &lt;code&gt;ABTest&lt;/code&gt; and &lt;code&gt;ABParameter&lt;/code&gt;, aptly named &lt;code&gt;AB&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# these are set by the configure() function&lt;/span&gt;
    &lt;span class="n"&gt;_id_provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;_storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="c1"&gt;# track the number of alternatives for each&lt;/span&gt;
    &lt;span class="c1"&gt;# named test; helps prevent errors where some&lt;/span&gt;
    &lt;span class="c1"&gt;# parameters have more alts than others&lt;/span&gt;
    &lt;span class="n"&gt;__n_per_test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alternatives&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;test_name&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;AB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__n_per_test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;AB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__n_per_test&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;test_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alternatives&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alternatives&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;AB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__n_per_test&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;test_name&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Wrong number of alternatives&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;test_name&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alternatives&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;alternatives&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_id_provider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_identity&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;alternative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;alternative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_alternative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;alternative&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;alternative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;randrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alternatives&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_alternative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alternative&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;alternative&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;alternative&lt;/code&gt; property here does most of the heavy lifting, it consults with the storage object (which, in 10gen's case, is backed by &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt;, of course) to find out if the current user already has been assigned an alternative; if not, it creates and stores one for the user. The &lt;code&gt;identity&lt;/code&gt; property consults an &lt;code&gt;IdentityProvider&lt;/code&gt; to identify the user; this might be implemented with cookies for sites that don't have logins, or might simply be a user ID for sites that do.&lt;/p&gt;
&lt;p&gt;Next up is &lt;code&gt;ABParameter&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ABParameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AB&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# a descriptor object which can be used to vary parameters&lt;/span&gt;
    &lt;span class="c1"&gt;# in a class definition according to A/B testing rules.&lt;/span&gt;
    &lt;span class="c1"&gt;#&lt;/span&gt;
    &lt;span class="c1"&gt;# each viewer who views the given class will always be&lt;/span&gt;
    &lt;span class="c1"&gt;# consistently shown the Nth choice from among the&lt;/span&gt;
    &lt;span class="c1"&gt;# alternatives, even between different attributes in the&lt;/span&gt;
    &lt;span class="c1"&gt;# class, so long as the name is the same between them&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__get__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alternatives&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alternative&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Python will call &lt;code&gt;__get__&lt;/code&gt; with a reference to the instance ("&lt;code&gt;self&lt;/code&gt;") and the owning class (in our example, the &lt;code&gt;Signup&lt;/code&gt; class), and return the value returned by this function when the attribute to which this descriptor is bound is accessed.&lt;/p&gt;
&lt;p&gt;The storage backends (currently MongoDB and filesystem are supported) are responsible for storing results from the tests, test configurations, and user-to-alternative assignments. The code there is not particularly exciting, so I won't show it here.&lt;/p&gt;
&lt;h1&gt;Collecting Results&lt;/h1&gt;
&lt;p&gt;Dabble's storage object has a &lt;code&gt;report&lt;/code&gt; method which summarizes the &lt;code&gt;record&lt;/code&gt;ed results in a dictionary like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;test_name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;signup_button&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;&amp;#39;results&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;alternative&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;red&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;funnel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;stage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;show&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;signup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;attempted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1813&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;converted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;
            &lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;alternative&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;green&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;&amp;#39;funnel&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;stage&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;show&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;signup&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;attempted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1838&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;&amp;#39;converted&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;
            &lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Each alternative's results has a &lt;code&gt;funnel&lt;/code&gt; element, which shows the number of users who attempted (i.e. the first step was recorded) and converted (i.e. the second step was recorded) for each successive pair of steps as defined in the &lt;code&gt;ABTest&lt;/code&gt;. Here we can see that about the same number of people attempted the show-signup stage for each alternative, but nearly twice as many people signed up with the green button.&lt;/p&gt;
&lt;h1&gt;What's Next?&lt;/h1&gt;
&lt;p&gt;Here are a few areas where I'd like to improve Dabble:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support non class-based views web frameworks (&lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt;, &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;, etc)&lt;/li&gt;
&lt;li&gt;Build &lt;code&gt;IdentityProvider&lt;/code&gt; support for common web frameworks&lt;/li&gt;
&lt;li&gt;DRY reporting (currently the two backends duplicate much of the same functionality in the &lt;code&gt;report&lt;/code&gt; method)&lt;/li&gt;
&lt;li&gt;Support other back-ends for storing results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can help! &lt;a href="https://github.com/dcrosta/dabble"&gt;fork the repository&lt;/a&gt; on &lt;a href="https://github.com/"&gt;GitHub&lt;/a&gt;, use it in your application, report or fix bugs, contribute improvements, etc.&lt;/p&gt;</content><category term="python"></category><category term="web development"></category></entry><entry><title>On Job "Requirements"</title><link href="https://late.am/post/2011/10/19/on-job-requirements.html" rel="alternate"></link><published>2011-10-19T00:00:00-04:00</published><updated>2011-10-19T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-10-19:/post/2011/10/19/on-job-requirements.html</id><summary type="html">&lt;p&gt;If you look at pretty much any job posting, you'll see some variation on this (in this case, for a hypothetical web programming position):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BSc in Computer Science or equivalent&lt;/li&gt;
&lt;li&gt;2-5 years web development experience&lt;/li&gt;
&lt;li&gt;Expert in Java, SQL, HTML, CSS, Javascript&lt;/li&gt;
&lt;li&gt;Strong communicator&lt;/li&gt;
&lt;li&gt;Able to multi-task&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The numbers, degrees, and technologies will vary from one job to another, but the basic form is almost always there.&lt;/p&gt;
&lt;p&gt;But what's the point of this section? I've never gotten a job for which I met all the "requirements," and I've never cared if the candidate I'm hiring doesn't meet them either. Is it just out of habit?&lt;/p&gt;
</summary><content type="html">&lt;p&gt;If you look at pretty much any job posting, you'll see some variation on this (in this case, for a hypothetical web programming position):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;BSc in Computer Science or equivalent&lt;/li&gt;
&lt;li&gt;2-5 years web development experience&lt;/li&gt;
&lt;li&gt;Expert in Java, SQL, HTML, CSS, Javascript&lt;/li&gt;
&lt;li&gt;Strong communicator&lt;/li&gt;
&lt;li&gt;Able to multi-task&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The numbers, degrees, and technologies will vary from one job to another, but the basic form is almost always there.&lt;/p&gt;
&lt;p&gt;But what's the point of this section? I've never gotten a job for which I met all the "requirements," and I've never cared if the candidate I'm hiring doesn't meet them either. Is it just out of habit?&lt;/p&gt;


&lt;p&gt;As an occasional job seeker, I've learned to ignore this, like web ads. It's filler with no relevance to my job search. I care about what a company can offer me: is the work exciting? Does the product have social value? Will I get paid well? Work with exceptional people who are excited about what they do? And do those people have lives outside of work, and interesting stories and experiences to share?&lt;/p&gt;
&lt;p&gt;If you're looking for a job and reading this, I recommend you do the same. If a job looks interesting and you think you deserve a company's consideration, don't be scared off by a list of "requirements." If you're a good fit, you'll get an interview and fair treatment. To turn away because of a bullet point or two is a shame and a waste.&lt;/p&gt;
&lt;p&gt;On the flip side, when screening and interviewing, I don't want to miss a strong candidate because they can't check a box in the HR application. Even if I did care about them, these "requirements" are weak signals, at best. I'm much more impressed by your history and your work: do you spend time contributing to open source projects? Have you done anything impressive at previous jobs, or on school projects? Does your resume, cover letter, and online presence show potential for an upwards trajectory in your career? Ultimately, do I think you can make a positive impact on the product, culture, and success of the company?&lt;/p&gt;
&lt;p&gt;A corollary, to all those writing job requisitions: you have only a few hundred words to make an impression on candidates. If you want the best, most excited, most motivated candidates, you'll have to work hard to impress them. Tell them why they should care about your company, and what you do. Tell them what will make them excited to get up and go to work every morning. Tell them, honestly, what you expect of them -- not a list of credentials, but a sampling of what they'll do and how you expect them to do it.&lt;/p&gt;
&lt;p&gt;With this advice in mind, I present a revised "requirements" section:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why we'll love you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You live and breathe web development. You've hit all the bumps in the road, and learned from each encounter what to do and what not to do. You've used enough frameworks, libraries, and platforms to have opinions, and you'll stand up for them.&lt;/li&gt;
&lt;li&gt;You scoff at static sites, and haven't built one in years. You know that content belongs in a database. Bonus points if you've used MongoDB.&lt;/li&gt;
&lt;li&gt;You read blogs, watch videos, attend meetups, and go to conferences, because you're a geek who never stops learning and improving your skillset.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Better, no?&lt;/p&gt;</content><category term="recruiting"></category></entry><entry><title>Never Use Source Control GUIs!</title><link href="https://late.am/post/2011/09/27/never-use-source-control-guis.html" rel="alternate"></link><published>2011-09-27T00:00:00-04:00</published><updated>2011-09-27T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-09-27:/post/2011/09/27/never-use-source-control-guis.html</id><summary type="html">&lt;p&gt;Just received this IM from my friend, &lt;a href="http://blog.ultranurd.net/"&gt;Nick&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kjsahfdlkjashdflkajshdflkjh&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;gui&lt;/span&gt; &lt;span class="n"&gt;itself&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;periodically&lt;/span&gt; &lt;span class="n"&gt;refreshes&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;forked&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Version control GUIs are &lt;em&gt;uniformly evil&lt;/em&gt;. They entice you with a multitude of colors, and 1-pixel lines; their siren song is one-click commits and graphical 3-way merges.&lt;/p&gt;
&lt;p&gt;But, like Odysseus, if we have any hope of prevailing, we must lash ourselves to the mast and steer past this temptation.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Just received this IM from my friend, &lt;a href="http://blog.ultranurd.net/"&gt;Nick&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;58&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kjsahfdlkjashdflkajshdflkjh&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;was&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;gui&lt;/span&gt; &lt;span class="n"&gt;itself&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;periodically&lt;/span&gt; &lt;span class="n"&gt;refreshes&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="n"&gt;AM&lt;/span&gt; &lt;span class="n"&gt;Nick&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;forked&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Version control GUIs are &lt;em&gt;uniformly evil&lt;/em&gt;. They entice you with a multitude of colors, and 1-pixel lines; their siren song is one-click commits and graphical 3-way merges.&lt;/p&gt;
&lt;p&gt;But, like Odysseus, if we have any hope of prevailing, we must lash ourselves to the mast and steer past this temptation.&lt;/p&gt;


&lt;p&gt;The real issue, of course, isn't the GUI per se, but the fact that a developer has virtually no idea what commands the GUI is issuing. I claim that any GUI which sufficiently exposes the underlying tool's features to know with confidence what clicking any button will do fails at being any more intuitive than the command line tool itself; and any tool which does not must by necessity contain hidden traps that an unwary developer will inevitably hit. Do not use GUIs for source control.&lt;/p&gt;
&lt;p&gt;In 5 years, something like 90% of the source control mishaps I've seen can be attributed to GUIs hiding complexity of what they're doing. Git can be a beast to learn, for sure; but at least once you develop an intuition, you can be reasonably safe in performing even advanced operations at the command line tool, in a way I've never felt (or felt from co-workers) about GUI tools.&lt;/p&gt;
&lt;p&gt;There is one place in which I think a GUI far outstrips the command line: read-only history and patch/diff viewing. I use &lt;a href="http://gitx.frim.nl/"&gt;GitX&lt;/a&gt; for this, though there may be others which are as good or better. (And, true to form, I always launch it from the command line with &lt;code&gt;gitx&lt;/code&gt;.)&lt;/p&gt;</content><category term="software development"></category><category term="version control"></category><category term="gui"></category><category term="command line"></category></entry><entry><title>Professor, a MongoDB Profile Viewer</title><link href="https://late.am/post/2011/09/22/professor-a-mongodb-profile-viewer.html" rel="alternate"></link><published>2011-09-22T00:00:00-04:00</published><updated>2011-09-22T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-09-22:/post/2011/09/22/professor-a-mongodb-profile-viewer.html</id><summary type="html">&lt;p&gt;I presented &lt;a href="https://github.com/dcrosta/professor"&gt;Professor&lt;/a&gt;, my &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; profile viewer and analyzer, this past Tuesday at the &lt;a href="http://www.meetup.com/New-York-MongoDB-User-Group/events/17102747/"&gt;New York MongoDB User Group&lt;/a&gt;. Professor aims to use the &lt;a href="/post/2011/09/20/the-new-profiler-in-mongodb-20"&gt;new features in the MongoDB 2.0 profiler&lt;/a&gt; to make profile information intelligible and actionable. It's open-source (BSD-licensed) and written in &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt; and &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I presented &lt;a href="https://github.com/dcrosta/professor"&gt;Professor&lt;/a&gt;, my &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; profile viewer and analyzer, this past Tuesday at the &lt;a href="http://www.meetup.com/New-York-MongoDB-User-Group/events/17102747/"&gt;New York MongoDB User Group&lt;/a&gt;. Professor aims to use the &lt;a href="/post/2011/09/20/the-new-profiler-in-mongodb-20"&gt;new features in the MongoDB 2.0 profiler&lt;/a&gt; to make profile information intelligible and actionable. It's open-source (BSD-licensed) and written in &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt; and &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;.&lt;/p&gt;


&lt;h1&gt;10,000 Feet&lt;/h1&gt;
&lt;p&gt;The primary design goal of Professor is to give you at-a-glance information about the performance of your queries (and, ultimately, updates and commands, though these are not yet supported). It presents a dashboard-style interface showing the worst-performing queries first, with aggregate timing information and a mini-histogram:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Professor Dashboard" src="/static/professor-dashboard.png"&gt;&lt;/p&gt;
&lt;p&gt;Here you can see that there are 3 collections in this example database, and one query that we've seen for each. The "skeleton" of the query is essentially the query without any values. Skeletons preserve the structure of the query and allow Professor to group same-structured queries together, to present their information in aggregate, even if the queries used different values.&lt;/p&gt;
&lt;p&gt;Immediately to the right of the skeleton is a log-scale histogram of the query times: the leftmost bucket represents queries that ran in less than 1ms, the next queries that ran less than 2ms, then 4ms, 8ms, etc. When using professor, you want to see histograms like the one for "indexdocs," which shows that all or nearly all queries executed in less than a millisecond.&lt;/p&gt;
&lt;h1&gt;Zooming In&lt;/h1&gt;
&lt;p&gt;You can click on the collection name to see only queries for that collection (along with some collection-specific information: number of documents, average size, total storage size, and the indexes that exist on the collection), or you can click on the query skeleton to see all queries collected which match that skeleton, and the time each one took to execute:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Collection and Query Information" src="/static/professor-queries.png"&gt;&lt;/p&gt;
&lt;p&gt;(Here you can see more clearly how the actual queries translate to the skeleton above.)&lt;/p&gt;
&lt;p&gt;So why are these queries taking so long? (Yes, even 3 milliseconds counts as "long"!) This page gives you all the information you need. (Hint: take a look at the number of documents and at the indexes).&lt;/p&gt;
&lt;h1&gt;Under the Hood&lt;/h1&gt;
&lt;p&gt;Professor attempts to limit its impact on your running database -- after all, one of the situations where you might need Professor's help is when your database is performing badly (although you should have investigated your slow queries far sooner!). Rather than repeatedly querying the &lt;code&gt;system.profile&lt;/code&gt; collection, Professor makes a local copy (preferably into a separate &lt;code&gt;mongod&lt;/code&gt; instance running on another server), and uses that database to serve all the web pages.&lt;/p&gt;
&lt;p&gt;Every time an update command is issued (either by clicking the "update now" link on the dashboard, or by the &lt;code&gt;profess&lt;/code&gt; command line script), Professor connects to the target database, queries only for &lt;code&gt;system.profile&lt;/code&gt; documents newer than the last time it looked, processes them (this is when Professor generates the skeleton) and inserts them into its local store.&lt;/p&gt;
&lt;h1&gt;Professor's Future&lt;/h1&gt;
&lt;p&gt;Professor is a work in progress. Here is a laundry list of features I hope to add, in no particular order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for other operation types (as I've already mentioned)&lt;/li&gt;
&lt;li&gt;Use update modifiers (&lt;code&gt;$inc&lt;/code&gt; and friends) to generate aggregate statistics during profile intake, rather than on page generation&lt;/li&gt;
&lt;li&gt;Use AJAX or simple page reloads to turn the static web view into a periodically-updating live view (like the UNIX &lt;code&gt;top&lt;/code&gt; command)&lt;/li&gt;
&lt;li&gt;Show other fields on the query list (&lt;code&gt;nscanned&lt;/code&gt;, &lt;code&gt;ntoreturn&lt;/code&gt;, &lt;code&gt;nreturned&lt;/code&gt;, &lt;code&gt;scanAndOrder&lt;/code&gt;, etc)&lt;/li&gt;
&lt;li&gt;Ability to replay a query with &lt;code&gt;explain()&lt;/code&gt; to get more detailed information, such as what index, if any, was used to service a query&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Or, &lt;em&gt;nudge nudge&lt;/em&gt;, you could &lt;a href="https://github.com/dcrosta/professor"&gt;fork Professor on GitHub&lt;/a&gt; and help me work on some of these yourself!&lt;/p&gt;</content><category term="mongodb"></category><category term="flask"></category><category term="python"></category></entry><entry><title>The new Profiler in MongoDB 2.0</title><link href="https://late.am/post/2011/09/20/the-new-profiler-in-mongodb-20.html" rel="alternate"></link><published>2011-09-20T00:00:00-04:00</published><updated>2011-09-20T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-09-20:/post/2011/09/20/the-new-profiler-in-mongodb-20.html</id><summary type="html">&lt;p&gt;One of my favorite features in &lt;a href="http://www.mongodb.org/downloads"&gt;MongoDB&lt;/a&gt; &lt;a href="http://www.mongodb.org/display/DOCS/2.0+Release+Notes"&gt;2.0&lt;/a&gt; is the finer-grained output of the database profiler. In earlier versions, the bulk of the profile information was contained within a (structured, parseable) string, but as of 2.0 the fields have been broken out and made queryable.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;One of my favorite features in &lt;a href="http://www.mongodb.org/downloads"&gt;MongoDB&lt;/a&gt; &lt;a href="http://www.mongodb.org/display/DOCS/2.0+Release+Notes"&gt;2.0&lt;/a&gt; is the finer-grained output of the database profiler. In earlier versions, the bulk of the profile information was contained within a (structured, parseable) string, but as of 2.0 the fields have been broken out and made queryable.&lt;/p&gt;


&lt;h1&gt;Back in time&lt;/h1&gt;
&lt;p&gt;In production versions of MongoDB up to and including 1.8, database profiler output looked like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ts&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Thu Jan 29 2009 15:19:45 GMT-0500 (EST)&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;info&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;query test.foo ntoreturn:0 reslen:102 nscanned:2 query: {} nreturned:2 bytes:86&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;millis&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;That &lt;code&gt;info&lt;/code&gt; string has a lot of useful information in it, but how can I get at it? It's &lt;a href="http://www.google.com/search?q=log+parsing"&gt;log parsing&lt;/a&gt; all over again. The &lt;a href="http://www.mongodb.org/display/DOCS/Database+Profiler"&gt;profiler documentation&lt;/a&gt; even suggests querying this collection with un-anchored regular expressions and server-side javascript, which can be painfully slow if you want to do anything automated with the profile information. Want to filter by date? Yeah, good luck.&lt;/p&gt;
&lt;h1&gt;Today&lt;/h1&gt;
&lt;p&gt;In 2.0, the profiler output looks like this (for the same fictional query as above):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ts&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ISODate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2009-01-29T15:19:45Z&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;op&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;ns&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;test.foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;query&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="s2"&gt;&amp;quot;$query&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;responseLength&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;nscanned&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;nreturned&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s2"&gt;&amp;quot;millis&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Much nicer. Now we have lots of data to get our hands on; we can filter by &lt;code&gt;ns&lt;/code&gt; to find only queries against certain collections, sort by &lt;code&gt;ts&lt;/code&gt; which is a proper, orderable date object; we can consider only queries slower or faster than a certain time, and we can even reach into the query itself and only look at operations touching a certain field, or containing a certain target value. Cool!&lt;/p&gt;
&lt;p&gt;The profiler also contains entries on updates, inserts, removes, commands (like &lt;a href="http://www.mongodb.org/display/DOCS/MapReduce"&gt;Map-Reduce&lt;/a&gt;, &lt;a href="http://www.mongodb.org/display/DOCS/Aggregation"&gt;group, count&lt;/a&gt;, etc).&lt;/p&gt;
&lt;h1&gt;Get Started with the Profiler&lt;/h1&gt;
&lt;p&gt;If you haven't yet, read &lt;a href="http://www.mongodb.org/display/DOCS/Database+Profiler"&gt;the documentation on the profiler&lt;/a&gt;. Even though it still references the old formats, it has a lot of useful information and warnings about using the profiler (hint: it has small, but non-zero, overhead on performance -- use with caution!).&lt;/p&gt;
&lt;p&gt;Next fire up the profiler on your database:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;slowms&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;This tells MongoDB to log all operations that took 5 or more milliseconds to the &lt;code&gt;system.profile&lt;/code&gt; collection. It's a capped collection which is initially set to 1MB. This is enough to capture around 4,000 small operations (for some reasonable value of "small").&lt;/p&gt;
&lt;p&gt;To make the profile collection larger, just drop it and explicitly create it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;system.profile&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;capped&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;{ "ok" : 1 }
    test&amp;gt; db.runCommand({profile: 1, slowms: 5})&lt;/p&gt;
&lt;p&gt;And you're back up with a 100MB profile (which holds &lt;em&gt;a lot&lt;/em&gt; of operations).&lt;/p&gt;
&lt;h1&gt;Wishlist&lt;/h1&gt;
&lt;p&gt;The profiler in MongoDB 2.0 has come a long way, but I'd like to see a few features added:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;logging of &lt;code&gt;nScannedObjects&lt;/code&gt;, as in the &lt;code&gt;explain()&lt;/code&gt; output, which will help diagnose queries that are hitting the collection too hard&lt;/li&gt;
&lt;li&gt;logging of the explain plan, or at least index, used to serve a query&lt;/li&gt;
&lt;li&gt;filtering options for entries going &lt;em&gt;into&lt;/em&gt; the profile, like a whitelist/blacklist approach by collection name, op type, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Coming Soon&lt;/h1&gt;
&lt;p&gt;I've written a web-based profile analyzer, named "Professor", that I'm presenting at tonight's &lt;a href="http://www.meetup.com/New-York-MongoDB-User-Group/events/17102747/"&gt;MongoDB User Group&lt;/a&gt;. A post about it should be up in the next few days.&lt;/p&gt;</content><category term="mongodb"></category></entry><entry><title>Hello, Plog</title><link href="https://late.am/post/2011/09/16/hello-plog.html" rel="alternate"></link><published>2011-09-16T00:00:00-04:00</published><updated>2011-09-16T00:00:00-04:00</updated><author><name>Dan Crosta</name></author><id>tag:late.am,2011-09-16:/post/2011/09/16/hello-plog.html</id><summary type="html">&lt;p&gt;I've wanted to start writing a blog for the past few months. By which I mean, writing blog posts. But why focus on writing great content when you can write great software instead?&lt;/p&gt;
&lt;p&gt;Allow me to introduce &lt;a href="https://github.com/dcrosta/plog"&gt;Plog&lt;/a&gt;, the software running &lt;a href="http://late.am/"&gt;late.am&lt;/a&gt;. Read on to see how &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;, &lt;a href="http://www.mongoengine.org/"&gt;Mongoengine&lt;/a&gt;, and &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; can work together to create something beautiful.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;I've wanted to start writing a blog for the past few months. By which I mean, writing blog posts. But why focus on writing great content when you can write great software instead?&lt;/p&gt;
&lt;p&gt;Allow me to introduce &lt;a href="https://github.com/dcrosta/plog"&gt;Plog&lt;/a&gt;, the software running &lt;a href="http://late.am/"&gt;late.am&lt;/a&gt;. Read on to see how &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;, &lt;a href="http://www.mongoengine.org/"&gt;Mongoengine&lt;/a&gt;, and &lt;a href="http://www.mongodb.org/"&gt;MongoDB&lt;/a&gt; can work together to create something beautiful.&lt;/p&gt;


&lt;h1&gt;Data Model&lt;/h1&gt;
&lt;p&gt;The data model for Plog is about as simple as you'd expect:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;pubdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BooleanField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;blurb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;views&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;allow_inheritance&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;&amp;#39;indexes&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;published&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;slug&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pubdate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fields&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;published&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;_words&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pubdate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Setting &lt;code&gt;allow_inheritance&lt;/code&gt; to &lt;code&gt;False&lt;/code&gt; instructs Mongoengine not to add two bookkeeping fields &lt;code&gt;_cls&lt;/code&gt; and &lt;code&gt;_types&lt;/code&gt; to the documents. In cases where many types of documents share a collection, Mongoengine uses these fields to filter results only to the type (or subtypes) of document corresponding to the Python class you are querying for.&lt;/p&gt;
&lt;p&gt;The first index will be used on virtually every page in the site. Individual post pages will always be queried using &lt;code&gt;published&lt;/code&gt; and &lt;code&gt;slug&lt;/code&gt;, and sorted by &lt;code&gt;pubdate&lt;/code&gt;; the homepage and archive pages don't query by &lt;code&gt;slug&lt;/code&gt;, but do sort by (and, in the case of archive pages, filter by) &lt;code&gt;pubdate&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;Search&lt;/h1&gt;
&lt;p&gt;The second index includes the &lt;code&gt;_words&lt;/code&gt; field, which is automatically generated by Plog when a post is saved. Indexes on arrays in MongoDB are called "multi-key" indexes, since each element of the array is given an entry in the index. This allows efficient queries into array fields, looking for one or more of the values.&lt;/p&gt;
&lt;p&gt;Plog's search is implemented using the &lt;a href="http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24all"&gt;&lt;code&gt;$all&lt;/code&gt; operator&lt;/a&gt; against &lt;code&gt;_words&lt;/code&gt;, which returns documents whose &lt;code&gt;_words&lt;/code&gt; array contains each of the search terms:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_words__all&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-pubdate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Plog doesn't support complex search use cases like stemming (although that would be easy to add using &lt;a href="http://www.nltk.org/"&gt;nltk&lt;/a&gt; or a similar tool), boolean queries, phrase searches, etc.&lt;/p&gt;
&lt;h1&gt;Tag Clouds&lt;/h1&gt;
&lt;p&gt;Each post contains its own tags, but to generate a tag cloud we need to know the distribution of tags among all the posts in order to size.&lt;/p&gt;
&lt;p&gt;A naive solution would be to query for all published posts, and aggregate the tag information in the application logic. However this requires sending all the posts over the network from the database to the application server which (and I'm being very optimistic here) could be a lot of data to transmit.&lt;/p&gt;
&lt;p&gt;A slightly better approach might use map-reduce to pre-aggregate the tag count information within the database, and generate a collection containing a mapping from tag name to tag count. Such a job might be triggered on a schedule, or each time a post is saved.&lt;/p&gt;
&lt;p&gt;Rather than either of these, Plog use MongoDB's atomic update operator &lt;code&gt;$inc&lt;/code&gt; along with upserts to maintain tag count information when posts are saved. The counts for the tags on the previous version of the post are each decremented by 1, and the counts for the tags on the new version of the post are each incremented by one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# decrement tagcloud count on all tags in the&lt;/span&gt;
    &lt;span class="c1"&gt;# previous version of the Post&lt;/span&gt;
    &lt;span class="n"&gt;TagCloud&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count__gte&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;inc__count&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set__updated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slug_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pubdate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# then increment tagcloud count on all tags in&lt;/span&gt;
    &lt;span class="c1"&gt;# the current version of the Post&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;TagCloud&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;inc__count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;set__updated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;upsert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Note that since upsert will create at most one document, we have to iterate the tags in the second set of updates; in the first, since we're only interested in modifying &lt;code&gt;TagCloud&lt;/code&gt; objects that might already exist, we don't need an upset, and can do a one-line update.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;TagCloud&lt;/code&gt; model is quite simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TagCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;A healthy sprinkling of math determines the "bucket" that each tag belongs in (with bucket 1 being the tags occurring most frequently to bucket 6 being the least frequent -- the bucket is used to generate an &lt;code&gt;h1&lt;/code&gt; through &lt;code&gt;h6&lt;/code&gt; tag in the template):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sizes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TagCloud&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count__gt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;

    &lt;span class="n"&gt;least&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;most&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;least&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sizes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sizes&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;  &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;least&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Atom Feed&lt;/h1&gt;
&lt;p&gt;Flask has built-in support for generating Atom XML feeds from (or, to be more accurate, &lt;a href="http://werkzeug.pocoo.org/"&gt;Werkzeug&lt;/a&gt;, the WSGI library beneath Flask, does)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AtomFeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;late.am&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;feed_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;feed&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_external&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Dan Crosta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dcrosta@late.am&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;static&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mug.png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_external&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;generator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;plog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;https://github.com/dcrosta/plog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-pubdate&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;feed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blurb&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;html&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Dan Crosta&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;email&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dcrosta@late.am&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_external&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;url_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;permalink&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_external&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;published&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pubdate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;h1&gt;Other Goodies&lt;/h1&gt;
&lt;p&gt;As a web developer, obviously, I dislike writing HTML. That's why Plog uses &lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt; powered by the &lt;a href="https://github.com/trentm/python-markdown2"&gt;&lt;code&gt;markdown2&lt;/code&gt; module&lt;/a&gt;. &lt;code&gt;markdown2&lt;/code&gt; supports code syntax highlighting using &lt;a href="http://pygments.org/"&gt;&lt;code&gt;pygments&lt;/code&gt;&lt;/a&gt;, which was an added bonus.&lt;/p&gt;</content><category term="mongodb"></category><category term="python"></category><category term="flask"></category></entry></feed>