<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>GrokCode</title>
    <description>Grok some code. Programming tutorials, long form articles, and opinion pieces about building things on the internet.
</description>
    <link>http://www.grokcode.com/</link>
    <atom:link href="http://www.grokcode.com/feed.xml" rel="self" type="application/rss+xml" />
    <pubDate>Sun, 29 Sep 2024 23:09:35 +0000</pubDate>
    <lastBuildDate>Sun, 29 Sep 2024 23:09:35 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>I ❤ Code Review</title>
        <description>&lt;p&gt;Code review has the proven benefit of reducing bugs, but there are so many more reasons to love code review. It is also a great way to improve code clarity, avoid the “hit by a bus” problem, learn new things, and solidify the team.&lt;!-- more --&gt;&lt;/p&gt;

&lt;h2 id=&quot;find-bugs&quot;&gt;Find bugs&lt;/h2&gt;

&lt;p&gt;Catching bugs early is probably the most talked about benefit of code review. The sooner bugs are found, the easier, faster, and cheaper it is to fix them. So the best place to catch bugs is in automated testing, and the next best place is during code review while the code is likely still fresh in your mind. Once defects move into staging or production and are caught by QA or real users, chances are you have long since moved on to developing something else and have to task switch back in and refresh your memory on how that particular area of the code works.&lt;/p&gt;

&lt;p&gt;There are a lot of statistics out there on the effectiveness of code review for detecting bugs. Here’s what Steve McConnell says in  &lt;a href=&quot;http://amzn.to/2BhlCCf&quot;&gt;Code Complete&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The average defect detection rate is only 25 percent for unit testing, 35 percent for function testing, and 45 percent for integration testing. In contrast, the average effectiveness of design and code inspections are 55 and 60 percent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Squashing bugs is important, but code review can do a lot more than that.&lt;/p&gt;

&lt;h2 id=&quot;improve-clarity&quot;&gt;Improve clarity&lt;/h2&gt;

&lt;p&gt;&lt;img class=&quot;alignleft&quot; src=&quot;/assets/img/miracle.jpg&quot; alt=&quot;Encourage comments during code review&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Code review can do a lot to improve the clarity and general quality of the codebase. Sometimes code is unnecessarily complex and another set of eyes will see a way to simplify it. Other times the code resists all attempts at complexity reduction. If the reviewer is having trouble understanding it and complexity can’t be reduced, it is probably a sign that there should be more comments.&lt;/p&gt;

&lt;p&gt;Another way clarity can be improved during code review is by communicating and enforcing coding standards. Most of these issues can be caught before the code review stage by using a linter as part of an automated test suite. This can catch a lot of different issues like misuse of whitespace, unused imports, malformed variables names, excessive line length, etc, but other coding standards are harder check for in an automated way.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.grokcode.com/assets/img/code-review-style.png&quot; alt=&quot;Enforce code style during code review&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;increase-the-bus-factor&quot;&gt;Increase the “bus factor”&lt;/h2&gt;

&lt;p&gt;The “bus factor” is the minimum number of team members that have to suddenly disappear from a project before the project stalls due to lack of knowledge.&lt;/p&gt;

&lt;p&gt;Having one person own a particular part of the codebase can be problematic if they aren’t available to help with modifications or bugfixes. Code review improves the bus factor because more team members have the opportunity to put eyes on each part of the code, ask questions, and think through how everything works during code review.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.grokcode.com/assets/img/busfactor1.png&quot; alt=&quot;Bus factor&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;img-attr&quot;&gt;Image attribution: &lt;a href=&quot;https://adamprescott.net/&quot;&gt;Adam Prescott&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;learn-new-things&quot;&gt;Learn new things&lt;/h2&gt;

&lt;p&gt;I’ve learned &lt;em&gt;tons&lt;/em&gt; of new things by having my code reviewed by other developers. Getting specific feedback or seeing examples of better ways to organize code, increase performance, or simplify logic is helpful when honing your craft.&lt;/p&gt;

&lt;p&gt;There is research showing that having someone critique your work is a valuable tool when working to become an expert. Anders Ericsson, a researcher who studies expert performance in all kinds of different domains, writes about the importance having someone who “understands and can demonstrate the proper way to perform various skills” and “who can provide useful feedback”. &lt;a href=&quot;http://amzn.to/2C6vwmR&quot;&gt;Peak: Secrets from the New Science of Expertise&lt;/a&gt; is full of Ericsson’s research-backed insight into becoming an expert.&lt;/p&gt;

&lt;p&gt;On the flip side, It’s OK to stretch the definition of “review” so that reviewers can learn too.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;h3 id=&quot;review&quot;&gt;re·view&lt;/h3&gt;
  &lt;ol&gt;
    &lt;li&gt;a formal assessment or examination of something with the possibility or intention of instituting change if necessary.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not every part of the review needs to be suggesting a change; asking questions is a good thing. Oftentimes the best way to learn is to ask. It could be general questions about the codebase, asking for more info on a particular library or tool, clarifying the need for a particular approach, or anything else.&lt;/p&gt;

&lt;p&gt;With the philosophy that no question is out of bounds when giving a code review, it is easier to spread knowledge around and level up everybody’s skills. It also has the awesome side-effect of making code reviews less confrontational and more collaborative.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.grokcode.com/assets/img/code-review-share-knowledge.png&quot; alt=&quot;Share knowledge during code review&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;praise-the-awesome&quot;&gt;Praise the awesome&lt;/h2&gt;

&lt;p&gt;Code reviews are a great venue for showing appreciation for all of your awesome team members. Super elegant solution? Refactor that removed a whole bunch of cruft? Particularly gnarly bug squashed? Perfect test coverage? Code review is a great place to share the love with a shoutout or a sprinkling of emojis that show how you feel about a changeset.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.grokcode.com/assets/img/code-review-praise.png&quot; alt=&quot;Give credit during a code review&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;reviews-for-everyone-everywhere&quot;&gt;Reviews for everyone, everywhere&lt;/h2&gt;

&lt;p&gt;With the widespread availability of excellent code review tools, there is really no reason &lt;em&gt;not&lt;/em&gt; to do them. For people already using GitHub, pull requests are an easy, lightweight way to incorporate code reviews into the development process. But there are plenty of other tools out there that work with different types of source control.&lt;/p&gt;

&lt;p&gt;Review everything, it’s worth it. ❤&lt;/p&gt;

</description>
        <pubDate>Thu, 08 Feb 2018 00:00:00 +0000</pubDate>
        <link>http://www.grokcode.com/why-i-love-code-review/</link>
        <guid isPermaLink="true">http://www.grokcode.com/why-i-love-code-review/</guid>
        
        
        <category>Tips &amp;amp; Tutorials</category>
        
      </item>
    
      <item>
        <title>Why Move From WordPress to Jekyll?</title>
        <description>&lt;p&gt;GrokCode has officially been a Jekyll site for a few months now. Why move from WordPress to a static Jekyll site hosted on GitHub Pages? Lots of reasons!&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-good&quot;&gt;The Good&lt;/h2&gt;

&lt;p&gt;A Jekyll site offers a number of advantages versus a WordPress site. One of the biggest wins is being able to switch from a shared hosting environment running WordPress to a static site hosted by GitHub.&lt;/p&gt;

&lt;p&gt;Static sites are faster than dynamic sites, and GitHub’s architecture has better uptime, a faster response rate, and is more scalable than shared hosting. It is also more hands off than managing a cloud hosted server. According to Pingdom, after moving from Bluehost to GitHub Pages, average response rates went from ~900ms to ~375ms, and there has been 0 downtime since the switch. This is pretty awesome, and the cherry on top is that GitHub Pages is free versus the $10 a month I was paying for Bluehost.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.grokcode.com/assets/img/github-uptime.png&quot; alt=&quot;GitHub uptime and response rate&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Another plus is I said goodbye to the last PHP environment I was responsible for maintaining. Sayonara PHP! After using WordPress on grokcode.com for nearly 10 years (wow!), I had accumulated a mishmash of custom plugins and PHP code. Moving to Jekyll was a great way to clean house and start fresh with Ruby and Liquid Templates. Unfortunately, I lost a few minor features along the way, but it looks like many of them are fixable and I’m hoping to implement solutions eventually.&lt;/p&gt;

&lt;p&gt;Moving to a git workflow that uses all of my regular development tools feels a lot more natural than editing in WordPress. Having templates, posts, assets, comments, and configuration for the site all under source control in one git repo is a nice way to organize things and has the bonus effect of improving my GitHub vanity metrics.&lt;/p&gt;

&lt;p&gt;What can I say, I love fake internet points!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.grokcode.com/assets/img/github-points.png&quot; alt=&quot;GitHub fake internet points&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;the-bad&quot;&gt;The Bad&lt;/h2&gt;

&lt;p&gt;There is a lot to love about a Jekyll site running on GitHub Pages, but I ran into a few drawbacks too.&lt;/p&gt;

&lt;h3 id=&quot;customization-takes-time&quot;&gt;Customization takes time&lt;/h3&gt;

&lt;p&gt;First, switching does take time. The whole process took me several months of wall time, but it was a backburner project that I sometimes let sit for weeks or more. The most time consuming part was porting my existing WordPress theme to Jekyll instead of using one of the many off-the-shelf &lt;a href=&quot;http://jekyllthemes.org/&quot;&gt;Jekyll Themes&lt;/a&gt;. This was a good way to learn liquid templating and level up my &lt;a href=&quot;http://sass-lang.com/&quot;&gt;Sass&lt;/a&gt; skills though, so it was worth it. Plus I was able to keep the same look and feel.&lt;/p&gt;

&lt;h3 id=&quot;some-features-are-not-straightforward&quot;&gt;Some features are not straightforward&lt;/h3&gt;

&lt;p&gt;Most of the features I used with WordPress can also be done with Jekyll, but often something that takes a few clicks in WordPress requires extensive customization to handle in Jekyll. One example is pinging aggregators when a new post is published. There is a &lt;a href=&quot;https://natelandau.com/how-to-notify-services-when-post-jekyll/&quot;&gt;way to do this&lt;/a&gt;, but it requires a custom Rakefile to generate the site instead of having GitHub do it.&lt;/p&gt;

&lt;p&gt;Another drawback specific to GitHub Pages with custom domains is that it requires a bit of trickery and a third-party service like Cloudflare to use SSL. There is a &lt;a href=&quot;https://www.jonathan-petitcolas.com/2017/01/13/using-https-with-custom-domain-name-on-github-pages.html&quot;&gt;good tutorial from Jonathan Petitcolas&lt;/a&gt; on setting that up. This is yet another item for my todo list.&lt;/p&gt;

&lt;h3 id=&quot;comments&quot;&gt;Comments&lt;/h3&gt;

&lt;p&gt;The next big missing piece is a comment system. The most common solution is a JavaScript commenting system like Disqus, but the appearance and functionality isn’t customizable to the level I was looking for, and the import of existing comments was painful. Also having the site comments live on, and be controlled by, a third-party service didn’t sit right with me.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignleft&quot; src=&quot;/assets/img/staticman-logo.png&quot; alt=&quot;staticman&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Enter &lt;a href=&quot;https://staticman.net/&quot;&gt;Staticman&lt;/a&gt;. The comment form is set up to POST to the Staticman service, which will then open a pull request on GitHub with the content of the comment in a format that the template can display. This is very cool! But…I’m currently looking at &lt;strong&gt;&lt;em&gt;293 PRs for spam comments&lt;/em&gt;&lt;/strong&gt; that I need to clean out. I tend to get a few of these a day, so it really needs a more robust solution. Staticman does have support for &lt;a href=&quot;https://en.wikipedia.org/wiki/Akismet&quot;&gt;akismet&lt;/a&gt;, but unfortunately at the moment it requires running your own Staticman instance.&lt;/p&gt;

&lt;p&gt;Migrating existing WordPress comments was possible after writing &lt;a href=&quot;https://github.com/grokcode/wordpress-to-staticman&quot;&gt;wordpress-to-staticman&lt;/a&gt;, a small conversion tool.&lt;/p&gt;

&lt;p&gt;Staticman also supports more advanced features that I have yet to set up like threaded comments and email notifications for replys. Again, this requires some changes to my templates and configuration that take more time and technical fortitude to set up than they do with WordPress.&lt;/p&gt;

&lt;h2 id=&quot;was-it-worth-it&quot;&gt;Was it worth it?&lt;/h2&gt;

&lt;p&gt;Definitely.&lt;/p&gt;

&lt;h4 id=&quot;pros&quot;&gt;Pros:&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;Better uptime&lt;/li&gt;
  &lt;li&gt;More scalable&lt;/li&gt;
  &lt;li&gt;Cheaper&lt;/li&gt;
  &lt;li&gt;No more PHP (yay)&lt;/li&gt;
  &lt;li&gt;Everything in source control&lt;/li&gt;
  &lt;li&gt;Improves vanity metrics&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;cons&quot;&gt;Cons:&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;Switching takes time&lt;/li&gt;
  &lt;li&gt;Custom code or configuration required for many features&lt;/li&gt;
  &lt;li&gt;Comment spam&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The improved performance, uptime, cost, and workflow far outweigh the cons. Plus I had a chance to learn a few things. GrokCode is happily a static Jekyll site hosted on GitHub pages.&lt;/p&gt;
</description>
        <pubDate>Thu, 11 Jan 2018 00:00:00 +0000</pubDate>
        <link>http://www.grokcode.com/grokcode-moves-to-github-pages/</link>
        <guid isPermaLink="true">http://www.grokcode.com/grokcode-moves-to-github-pages/</guid>
        
        
        <category>Extras</category>
        
      </item>
    
      <item>
        <title>Snakefooding Python Code For Complexity Visualization</title>
        <description>&lt;p&gt;&lt;a href=&quot;http://furius.ca/snakefood/&quot;&gt;Snakefood&lt;/a&gt; is a tool written by Martin Blais to create Python dependency graphs. Combined with GraphViz, snakefood can create beautiful visualizations of Python codebases. Here are graphs for some notable open source projects written in Python.&lt;!--more--&gt;&lt;/p&gt;

&lt;h2 id=&quot;python-web-frameworks&quot;&gt;Python Web Frameworks&lt;/h2&gt;

&lt;p&gt;The different development philosophies of Bottle, Django, Flask, and Pyramid are apparent by looking at their snakefood graphs.&lt;/p&gt;

&lt;h3 id=&quot;bottle&quot;&gt;Bottle&lt;/h3&gt;

&lt;p&gt;Bottle is a fast and simple micro framework for Python web applications.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/bottle.png&quot; alt=&quot;bottle&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-866&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/defnull/bottle&quot;&gt;Bottle&lt;/a&gt; release 0.12.7&lt;/small&gt;&lt;/p&gt;

&lt;h3 id=&quot;django&quot;&gt;Django&lt;/h3&gt;

&lt;p&gt;Django is a batteries-included web framework for perfectionists with deadlines.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/django.png&quot; alt=&quot;django&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-f ull wp-image-865&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/django/django&quot;&gt;Django&lt;/a&gt; release 1.7c3&lt;/small&gt;&lt;/p&gt;

&lt;h3 id=&quot;flask&quot;&gt;Flask&lt;/h3&gt;

&lt;p&gt;Flask is a microframework for Python.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/flask.png&quot; alt=&quot;flask&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-869&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/mitsuhiko/flask&quot;&gt;Flask&lt;/a&gt; release 0.10.1&lt;/small&gt;&lt;/p&gt;

&lt;h3 id=&quot;pyramid&quot;&gt;Pyramid&lt;/h3&gt;

&lt;p&gt;Pyramid is a small, fast, down-to-earth, open source Python web framework. It makes real-world web application development and deployment more fun, more predictable, and more productive.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/pyramid.png&quot; alt=&quot;pyramid&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-872&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/Pylons/pyramid&quot;&gt;Pyramid&lt;/a&gt; release 1.5.1&lt;/small&gt;&lt;/p&gt;

&lt;h2 id=&quot;queueing-implementations&quot;&gt;Queueing Implementations&lt;/h2&gt;

&lt;p&gt;Hat tip to Sylvain Zimmer for this deck on &lt;a href=&quot;http://www.slideshare.net/sylvinus/why-and-how-pricing-assistant-migrated-from-celery-to-rq-parispy-2&quot;&gt;switching from Celery to RQ&lt;/a&gt; which was how I first found out about snakefood.&lt;/p&gt;

&lt;h3 id=&quot;celery&quot;&gt;Celery&lt;/h3&gt;

&lt;p&gt;Celery is a complex feature-rich distributed queueing implementation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/celery.png&quot; alt=&quot;celery&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-867&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/celery/celery&quot;&gt;Celery&lt;/a&gt; release 3.0.20&lt;/small&gt;&lt;/p&gt;

&lt;h3 id=&quot;rq&quot;&gt;RQ&lt;/h3&gt;

&lt;p&gt;RQ is a simple Python library for queueing jobs and processing them in the background with workers.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/rq.png&quot; alt=&quot;rq&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-874&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/nvie/rq&quot;&gt;RQ&lt;/a&gt; release 0.4.6&lt;/small&gt;&lt;/p&gt;

&lt;h2 id=&quot;other-python-codebases&quot;&gt;Other Python Codebases&lt;/h2&gt;

&lt;p&gt;For curiosity’s sake I ran snakefood on a few other notable Python codebases.&lt;/p&gt;

&lt;h3 id=&quot;twisted&quot;&gt;Twisted&lt;/h3&gt;

&lt;p&gt;Twisted is an event-based framework for internet applications.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/twisted.png&quot; alt=&quot;twisted&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-875&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/twisted/twisted&quot;&gt;Twisted&lt;/a&gt; release 14.0.0&lt;/small&gt;&lt;/p&gt;

&lt;h3 id=&quot;mercurial&quot;&gt;Mercurial&lt;/h3&gt;

&lt;p&gt;Mercurial is a distributed source control management tool.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/mercurial.png&quot; alt=&quot;mercurial&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-871&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; release 3.1&lt;/small&gt;&lt;/p&gt;

&lt;h3 id=&quot;requests&quot;&gt;Requests&lt;/h3&gt;

&lt;p&gt;Requests is HTTP requests for humans. It is frequently mentioned as an example of an elegant, Pythonic codebase.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/requests.png&quot; alt=&quot;requests&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-873&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/kennethreitz/requests&quot;&gt;Requests&lt;/a&gt; release 2.3.0&lt;/small&gt;&lt;/p&gt;

&lt;h3 id=&quot;ipython&quot;&gt;IPython&lt;/h3&gt;

&lt;p&gt;IPython is an alternative to the standard Python shell. If you aren’t using IPython, you should be. &lt;a href=&quot;http://grokcode.com/811/you-should-change-your-python-shell/&quot;&gt;Here’s why IPython is awesome&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ipython.png&quot; alt=&quot;ipython&quot; width=&quot;756&quot; height=&quot;584&quot; class=&quot;alignleft size-full wp-image-870&quot; /&gt;&lt;small&gt;&lt;a href=&quot;https://github.com/ipython/ipython&quot;&gt;IPython&lt;/a&gt; release 2.2.0&lt;/small&gt;&lt;/p&gt;

&lt;h2 id=&quot;what-does-this-mean&quot;&gt;What does this mean?&lt;/h2&gt;

&lt;p&gt;Snakefood graphs are helpful to visualize both the approximate size of a codebase and the level to which concerns are separated. As a measure of code complexity, snakefood graphs show more information than lines of code (which is not that great of a metric), but at the cost of introducing more room for interpretation than a strictly numeric metric.&lt;/p&gt;

&lt;p&gt;The other problem with using snakefood graphs as a measure of code complexity is that it looks at code at the file level. A well-organized codebase containing many small files will look more complex than a codebase with a few very large but extremely long, complex files. Code with high testing coverage also tends to look more complex since the tests are dependent on so many parts of the code. Test and documentation modules were not included in any of the graphs above for this reason.&lt;/p&gt;

&lt;p&gt;In cases where a more careful measure of code complexity is required, something like &lt;a href=&quot;https://en.wikipedia.org/wiki/Cyclomatic_complexity&quot;&gt;cyclomatic complexity&lt;/a&gt; is more appropriate. The graphs created by snakefood are actually similar to what would be used to compute cyclomatic complexity as applied to files instead of functions or classes which is the more usual way to do it.&lt;/p&gt;

&lt;p&gt;Instead of using snakefood as a tool for visualizing complexity, the creator of snakefood uses it more to prevent unnecessary dependencies from creeping into the code:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Producing pretty graphs is fun, but I found the most leverage of it when I try to make my code simpler, I generate the graph and inspect unexpected dependencies and try to refactor my code to simply the dependency graph as much as possible (see furius.ca/beancount for a recent example). Enforcing rules can be done by writing a custom script against the snakefood output, as it’s very easy to accidentally introduce unwanted dependencies. I think the result is code that is better organized at a high level. &lt;cite&gt;–Martin Blais&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So snakefood graphs are are (sometimes) beautiful. They are a fairly quick ad hoc way of visualizing code complexity, and the snakefood output can be used to help identify and enforce dependency relationships. All in all, snakefood is a pretty awesome tool to have in your Python toolbox.&lt;/p&gt;
</description>
        <pubDate>Tue, 26 Aug 2014 13:51:03 +0000</pubDate>
        <link>http://www.grokcode.com/864/snakefooding-python-code-for-complexity-visualization/</link>
        <guid isPermaLink="true">http://www.grokcode.com/864/snakefooding-python-code-for-complexity-visualization/</guid>
        
        <category>bottle</category>
        
        <category>celery</category>
        
        <category>complexity</category>
        
        <category>django</category>
        
        <category>flask</category>
        
        <category>ipython</category>
        
        <category>mercurial</category>
        
        <category>pyramid</category>
        
        <category>python</category>
        
        <category>requests</category>
        
        <category>RQ</category>
        
        <category>snakefood</category>
        
        <category>twisted</category>
        
        
        <category>Books &amp;amp; Tools</category>
        
      </item>
    
      <item>
        <title>How To Use Django&amp;#8217;s Signing Functions For One Click Unsubscribes</title>
        <description>&lt;p&gt;Sometimes it is handy to allow users to change part of their profile without having to log in, as long as we can be reasonably sure of their identity. The most common use case is to unsubscribe from email newsletters, where it is annoying to have to login first.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;The workflow we want to handle looks like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;User clicks an unsubscribe link.&lt;/li&gt;
  &lt;li&gt;We verify the security token on the unsubscribe link.&lt;/li&gt;
  &lt;li&gt;The user gets a message saying they have been unsubscribed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow is pretty easy to set up using Django’s signing functions.&lt;/p&gt;

&lt;h2 id=&quot;creating-the-signed-url&quot;&gt;Creating the signed url&lt;/h2&gt;

&lt;p&gt;Let’s use Djanogo’s built in signing functions to create an unsubscribe link with a security token, and then create a way to verify the token. You can tuck the following code into the user profile.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.core.signing&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimestampSigner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BadSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SignatureExpired&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_unsubscribe_link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user_signups.views.unsubscribe&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                   &lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;token&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,})&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;make_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TimestampSigner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;check_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;%s:%s&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TimestampSigner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unsign&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_age&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Valid for 2 days
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BadSignature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SignatureExpired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;As long as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SECRET_KEY&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;django.conf.settings&lt;/code&gt; is kept private, it isn’t feasible for someone malicious to craft signed links.&lt;/p&gt;

&lt;p&gt;While verifying the token in the unsubscribe link, we want to strike a good balance with user friendliness and security. In the common case, a user shouldn’t have to login to unsubscribe. But we also want to prevent users from being accidentally unsubscribed by others if they forward the email or otherwise make it public. Adding an expiry date to the token somewhat mitigates this risk. Above we use a 2 day expiry date.&lt;/p&gt;

&lt;h2 id=&quot;the-url-pattern&quot;&gt;The url pattern&lt;/h2&gt;

&lt;p&gt;Now hooking up the url pattern which accepts the special characters that may appear in the token:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;^unsubscribe/(?P&amp;lt;username&amp;gt;[\w.@+-]+)/(?P&amp;lt;token&amp;gt;[\w.:\-_=]+)/$&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
     &lt;span class=&quot;s&quot;&gt;&apos;user_signups.views.unsubscribe&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-view&quot;&gt;The view&lt;/h2&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unsubscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot; 
    User is immediately unsubscribed if they are logged in as username, or
    if they came from an unexpired unsubscribe link. Otherwise, they are
    redirected to the login page and unsubscribed as soon as they log in.
    &quot;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_object_or_404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;is_active&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_authenticated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;check_token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
       &lt;span class=&quot;c1&quot;&gt;# unsubscribe them
&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get_profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newsletter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
       &lt;span class=&quot;n&quot;&gt;profile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

       &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;registration/unsubscribe.html&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;# Otherwise redirect to login page
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;next_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;user_signups.views.unsubscribe&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
                       &lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;token&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,})&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpResponseRedirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;%s?next=%s&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reverse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;login&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-templates&quot;&gt;The templates&lt;/h2&gt;

&lt;p&gt;In the email templates, use the unsubscribe link &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{{user.get_profile.create_unsubscribe_link}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;registration/unsubscribe.html&lt;/code&gt; add a message confirming that the user has been unsubscribed.&lt;/p&gt;

&lt;h2 id=&quot;other-notes&quot;&gt;Other notes&lt;/h2&gt;

&lt;p&gt;This type of signed link could also be used to improve the customer’s flow when upgrading SaaS plans, participating in a survey, or performing some other action that updates their profile when they click on a link in an email. Depending on what they are doing, you can add an additional security layer by checking that the ip address is one they have previously used while logging in.&lt;/p&gt;
</description>
        <pubDate>Thu, 13 Feb 2014 16:04:27 +0000</pubDate>
        <link>http://www.grokcode.com/819/one-click-unsubscribes-for-django-apps/</link>
        <guid isPermaLink="true">http://www.grokcode.com/819/one-click-unsubscribes-for-django-apps/</guid>
        
        <category>cryptography</category>
        
        <category>django</category>
        
        <category>email</category>
        
        <category>newsletter</category>
        
        <category>python</category>
        
        <category>unsubscribe</category>
        
        
        <category>Tips &amp;amp; Tutorials</category>
        
      </item>
    
      <item>
        <title>You Should Change Your Python Shell</title>
        <description>&lt;p&gt;If you write Python code, switching to IPython is the number one thing you can do to immediately improve your productivity. Bold words, I know. Let’s look at how IPython can make you a more productive programmer.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;If you are mostly a web developer or are new to Python, the &lt;a href=&quot;http://ipython.org/&quot;&gt;IPython homepage&lt;/a&gt; does not make it obvious how awesome IPython is (even if you don’t do any scientific computing!). If you have never used IPython before, forget about notebooks, data visualization, embeddable interpreters, and parallel computing. It’s all great stuff. But just forget about it for now.&lt;/p&gt;

&lt;p&gt;The absolute best thing about IPython is the shell. Here is a standard Python shell.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/python-shell2.png&quot; alt=&quot;Python shell&quot; width=&quot;693&quot; height=&quot;419&quot; class=&quot;img-noborder size-full wp-image-816&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And below is IPython. They look pretty similar, but IPython has a whole bunch of extras that will save you a vast amount of time and give you an immediate bump in productivity.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ipython-shell.png&quot; alt=&quot;IPython shell&quot; width=&quot;693&quot; height=&quot;419&quot; class=&quot;img-noborder size-full wp-image-813&quot; /&gt;&lt;/p&gt;

&lt;p&gt;OK so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;easy_install ipython[all]&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install ipython&lt;/code&gt; to get started. If any issues crop up, refer to the official &lt;a href=&quot;http://ipython.org/ipython-doc/stable/install/install.html&quot;&gt;install guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now let’s look at some of IPython’s most useful features that are applicable to everyone who uses a Python shell.&lt;/p&gt;

&lt;h2 id=&quot;persistent-history&quot;&gt;Persistent History&lt;/h2&gt;

&lt;p&gt;Command history is preserved when IPython is restarted, so you can use the up key to retrieve commands from the previous session. This will cut down on a lot of typing if your workflow looks like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Test something in the shell&lt;/li&gt;
  &lt;li&gt;Use an editor to make some code changes&lt;/li&gt;
  &lt;li&gt;Exit the python shell&lt;/li&gt;
  &lt;li&gt;Rerun the python shell so that the new code is loaded&lt;/li&gt;
  &lt;li&gt;Retype the test code&lt;/li&gt;
  &lt;li&gt;Test something in the shell again&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But actually, if you are using IPython, you won’t need to restart the python shell either. You can just enable autoreload.&lt;/p&gt;

&lt;h2 id=&quot;automatic-module-reloading&quot;&gt;Automatic Module Reloading&lt;/h2&gt;

&lt;p&gt;IPython will automatically pull in changes to your code files. To enable it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;In [1]: %load_ext autoreload
In [2]: %autoreload 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The workflow from above now looks like this:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Test something in the shell&lt;/li&gt;
  &lt;li&gt;Use an editor to make some code changes&lt;/li&gt;
  &lt;li&gt;Test something in the shell again&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you want autoreload enabled by default, you can edit your IPython profile to contain these lines:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;c.InteractiveShellApp.exec_lines = []
c.InteractiveShellApp.exec_lines.append(&apos;%load_ext autoreload&apos;)
c.InteractiveShellApp.exec_lines.append(&apos;%autoreload 2&apos;)&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;builtin-profiling-tools&quot;&gt;Builtin Profiling Tools&lt;/h2&gt;

&lt;p&gt;IPython comes with some “magic functions” to profile execution time and memory.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%time&lt;/code&gt; times how long it takes to run a statement.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%timeit&lt;/code&gt; times how long it takes to run a statement, but will average over many runs. It is smart enough to use a lot of runs for statements that don’t take very long, but only a few runs when execution time is high.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%prun&lt;/code&gt; gives a breakdown of execution time per function.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%lprun&lt;/code&gt; gives a breakdown of execution time per line.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%mprun&lt;/code&gt; gives a breakdown of memory usage per line.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%memit&lt;/code&gt; gives a breakdown of memory usage averaged over many runs.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%mprun&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%lprun&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%memit&lt;/code&gt; require some extra dependencies and a bit of configuration to enable, see details &lt;a href=&quot;http://pynash.org/2013/03/06/timing-and-profiling.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ipython-profiling2.png&quot; alt=&quot;Execution time and memory profiling with IPython&quot; width=&quot;693&quot; height=&quot;419&quot; class=&quot;img-noborder size-full wp-image-817&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;smart-copy-and-paste&quot;&gt;Smart Copy and Paste&lt;/h2&gt;

&lt;p&gt;You can paste chunks of code directly into IPython and it will properly handle the indentation. Even if each line has leading indents. So, you can copy and paste a few lines from the inside of a function or loop and IPython will ignore the leading indents that aren’t meaningful, but preserve the ones that are. Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%paste&lt;/code&gt; and/or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%cpaste&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;smart-indentation&quot;&gt;Smart Indentation&lt;/h2&gt;

&lt;p&gt;IPython will start the next line in the right place when you do a carriage return after a function definition, loop, if statement, etc. This is a nice convenience that saves a few keystrokes.&lt;/p&gt;

&lt;h2 id=&quot;tab-completion&quot;&gt;Tab Completion&lt;/h2&gt;

&lt;p&gt;You can use tab completion to explore objects and autocomplete keywords.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/ipython-tab-complete2.png&quot; alt=&quot;IPython tab completion&quot; width=&quot;693&quot; height=&quot;419&quot; class=&quot;img-noborder size-full wp-image-818&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;just-type-exit&quot;&gt;Just type exit&lt;/h2&gt;

&lt;p&gt;In the standard Python shell, you have to type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit()&lt;/code&gt;, but in IPython if you forget the parens and just say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;, that’s fine too. It’s a small detail, but details matter.&lt;/p&gt;

&lt;h2 id=&quot;more&quot;&gt;More&lt;/h2&gt;

&lt;p&gt;IPython has loads of other features, some will likely be useful to you, others maybe not so much unless you do scientific computing. This article just touched the very tip of the iceberg. To learn more about what IPython can do, have a look at &lt;a href=&quot;http://www.amazon.com/gp/product/1782169938/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1782169938&amp;amp;linkCode=as2&amp;amp;tag=grok-20&quot;&gt;Learning IPython for Interactive Computing and Data Visualization&lt;/a&gt; and the &lt;a href=&quot;https://github.com/ipython/ipython/wiki/Cookbook%3A-Index&quot;&gt;IPython Cookbook&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Mon, 16 Dec 2013 13:09:59 +0000</pubDate>
        <link>http://www.grokcode.com/811/you-should-change-your-python-shell/</link>
        <guid isPermaLink="true">http://www.grokcode.com/811/you-should-change-your-python-shell/</guid>
        
        <category>ipython</category>
        
        <category>python</category>
        
        <category>shells</category>
        
        
        <category>Books &amp;amp; Tools</category>
        
      </item>
    
      <item>
        <title>Deploy with &amp;#8216;Git Push&amp;#8217;</title>
        <description>&lt;p&gt;Single command deploys are awesome. When setting up a testing or production server, I always setup a script to deploy in a single command because it makes life easier and encourages frequent deploys.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;There are many different ways of setting up one click deploys. This tutorial covers how to setup a git postcommit hook that deploys on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push prod&lt;/code&gt;, which is pretty simple to get started with, but won’t scale well once you start running multiple app servers. For alternatives, you might have a look at &lt;a href=&quot;http://docs.fabfile.org/en/1.4.3/index.html&quot;&gt;Fabric&lt;/a&gt; or &lt;a href=&quot;http://capistranorb.com/&quot;&gt;Capistrano&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;setup-the-git-repo-on-the-server&quot;&gt;Setup the git repo on the server&lt;/h2&gt;

&lt;p&gt;If you don’t already have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; installed on the server, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; in and do a&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get install git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we want to create a git user:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo adduser \
    --system \
    --shell /bin/sh \
    --gecos &apos;git version control&apos; \
    --group \
    --disabled-password \
    --home /var/git \
    git
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And append your ssh public key to the git user’s list of authorized keys located at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/git/.ssh/authorized_keys&lt;/code&gt;, creating the file if necessary.&lt;/p&gt;

&lt;p&gt;Now let’s create the repository:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cd /var/git
sudo -u git mkdir &amp;lt;repo-name&amp;gt;.git
cd &amp;lt;repo-name&amp;gt;.git
sudo -u git git init --bare
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The repository here is bare so that it can accept pushes. See this &lt;a href=&quot;http://www.bitflop.com/document/111&quot;&gt;discussion of bare vs. non-bare repos&lt;/a&gt; if you are interested in learning more.&lt;/p&gt;

&lt;h2 id=&quot;create-a-hook&quot;&gt;Create a hook&lt;/h2&gt;

&lt;p&gt;OK so now we want to create the script that will run after a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; is received.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/git/&amp;lt;repo-name&amp;gt;.git/hooks/post-receive&lt;/code&gt; and give it these contents:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/bash
export GIT_WORK_TREE=/var/www/&amp;lt;app-name&amp;gt;/project/
git checkout -f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above assumes that your server is setup with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/&amp;lt;app-name&amp;gt;/project/&lt;/code&gt; as the location where your app server will be looking for your code files.&lt;/p&gt;

&lt;p&gt;Add execute permissions and make sure it is owned by git:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo chmod u+x hooks/post-receive
sudo chown git:git hooks/post-receive
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;handle-permissions&quot;&gt;Handle permissions&lt;/h2&gt;

&lt;p&gt;The post receive script will be run as git, so we need to make sure that the git user has permissions to write to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GIT_WORK_TREE&lt;/code&gt;, and also that the server can read and write from those directories. To handle this, lets make git a member of the nginx group, and then have the post receive script touch up the file permissions after checkout.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo usermod -a -G nginx $USER
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then edit the script to look like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Checkout the repo.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_WORK_TREE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/www/&amp;lt;app-name&amp;gt;/project/
git checkout &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Fix up permissions.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /var/www/&amp;lt;app-name&amp;gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; g+w project/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; git:nginx project/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;prepare-development-box&quot;&gt;Prepare development box&lt;/h2&gt;

&lt;p&gt;Now on your development machine, add the new remote repository and push to it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git remote add prod ssh://git@&amp;lt;server&amp;gt;/var/git/&amp;lt;repo-name&amp;gt;.git
git push prod master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;other-deploy-tasks&quot;&gt;Other deploy tasks&lt;/h2&gt;

&lt;p&gt;Often there will be other tasks that have to happen during the deploy. Here is an example of another post-receive hook script that I use for one of my Django projects. It collects the static files to prepare them to be served via nginx, runs the database migrations, and installs any new python requirements.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Checkout the repo.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GIT_WORK_TREE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/www/&amp;lt;app-name&amp;gt;/project/
git checkout &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Fix up permissions.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$GIT_WORK_TREE&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; g+w &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;py
&lt;span class=&quot;nb&quot;&gt;chown&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-R&lt;/span&gt; git:nginx &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;py

&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; ../env/bin/activate

&lt;span class=&quot;c&quot;&gt;# Collect static files&lt;/span&gt;
./manage.py collectstatic &lt;span class=&quot;nt&quot;&gt;--noinput&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; 0

&lt;span class=&quot;c&quot;&gt;# Handle DB Migrations&lt;/span&gt;
./manage.py syncdb &lt;span class=&quot;nt&quot;&gt;--migrate&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; 0

&lt;span class=&quot;c&quot;&gt;# Update requirements&lt;/span&gt;
pip &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; requirements.txt

deactivate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So there it is – a quick way to setup one touch deploys with git.&lt;/p&gt;
</description>
        <pubDate>Mon, 05 Nov 2012 07:08:29 +0000</pubDate>
        <link>http://www.grokcode.com/792/deploy-with-git-push/</link>
        <guid isPermaLink="true">http://www.grokcode.com/792/deploy-with-git-push/</guid>
        
        <category>deploy</category>
        
        <category>devops</category>
        
        <category>django</category>
        
        <category>fab</category>
        
        <category>git</category>
        
        <category>python</category>
        
        
        <category>Tips &amp;amp; Tutorials</category>
        
      </item>
    
      <item>
        <title>How to Setup a Linux, Nginx, uWSGI, Python, Django Server</title>
        <description>&lt;p&gt;This is a tutorial for setting up a Linux, Nginx, uWSGI, Python, Django server with a PostgreSQL database. This is the easy, straightforward approach to server setup and deployment. The intended audience is developers or teams who need to get an application or two running in production without too much hassle.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;If you need to oversee multiple servers or you are handling a large number of applications, the limitations of the setup described here will start to show through. &lt;a href=&quot;http://hynek.me/articles/python-app-deployment-with-native-packages/&quot;&gt;Here&lt;/a&gt; is an example of a much more complex deployment workflow that is more appropriate reading for dedicated DevOps people.&lt;/p&gt;

&lt;h2 id=&quot;general-server-setup&quot;&gt;General server setup&lt;/h2&gt;

&lt;p&gt;I’m using &lt;a href=&quot;http://www.linode.com/?r=e748550930d0390f86a171bc3465ce8bc0e211a6&quot;&gt;Linode&lt;/a&gt; running Ubuntu 12.04 LTS, but you should be able to adapt this tutorial to other hosts and distros as needed.&lt;/p&gt;

&lt;p&gt;Before getting started, take steps to update and secure the server. Security is beyond the scope of this article, but if you don’t know where to start, have a look at &lt;a href=&quot;http://library.linode.com/securing-your-server&quot;&gt;this tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;install-uwsgi&quot;&gt;Install uWSGI&lt;/h2&gt;

&lt;p&gt;Let’s get started by installing uWSGI. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh&lt;/code&gt; into the server and&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install uwsgi uwsgi-plugin-python&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create a user to run uWSGI:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo useradd -c &apos;uwsgi user&apos; -g nginx --system \
--no-create-home --disabled-login --disabled-password uwsgi&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create an upstart configuration file at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init/uwsgi.conf&lt;/code&gt; which looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;description &quot;uWSGI&quot;
start on runlevel [2345]
stop on runlevel [06]

respawn

exec uwsgi --master --processes 4 --die-on-term --uid uwsgi --gid nginx \
--socket /tmp/uwsgi.sock --chmod-socket 660 --vhost --logto /var/log/uwsgi.log \
--plugins python&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;install-nginx&quot;&gt;Install Nginx&lt;/h2&gt;

&lt;p&gt;The version of Nginx provided in the official Ubuntu repository is a bit outdated, so we are going to use the latest stable package from the Nginx repository. If you are on Ubuntu, just follow the instructions below, but if you are on another distro or run into trouble, have a look at the &lt;a href=&quot;http://nginx.org/en/download.html&quot;&gt;official install guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First we add the key&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;lanugae-bash&quot;&gt;wget http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then open up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/apt/sources.list&lt;/code&gt; file and append the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;deb http://nginx.org/packages/ubuntu/ precise nginx
deb-src http://nginx.org/packages/ubuntu/ precise nginx&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you aren’t using Ubuntu 12.04, change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;precise&lt;/code&gt; to the the codename for your Ubuntu version.&lt;/p&gt;

&lt;p&gt;Then install Nginx with:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install nginx&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/Screenshot-Welcome-to-nginx-Chromium1.png&quot; alt=&quot;Welcome to Nginx screen&quot; width=&quot;400&quot; height=&quot;391&quot; class=&quot;size-full wp-image-791&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Nginx is already running. To verify, open your server’s ip address in a browser, and you should see a ‘Welcome to nginx!’ screen.&lt;/p&gt;

&lt;h2 id=&quot;nginx-config&quot;&gt;Nginx config&lt;/h2&gt;

&lt;p&gt;The webapp is going to live at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/&amp;lt;app-name&amp;gt;/&lt;/code&gt;. Here is what the directory tree will look like when we finish:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/var/www/&amp;lt;app-name&amp;gt;/
    |-- env/                    The virtual env
    |-- project/                The Django project files
        |-- &amp;lt;apps&amp;gt;/
        |-- &amp;lt;project&amp;gt;/
            |-- wsgi.py
        |-- requirements.txt    Pip project dependency list
    |-- static/                 Static files to be served by nginx     
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So let’s setup that directory structure.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo mkdir /var/www/&amp;lt;app-name&amp;gt;
sudo mkdir /var/www/&amp;lt;app-name&amp;gt;/project
sudo mkdir /var/www/&amp;lt;app-name&amp;gt;/static&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now to setup the virtual environment for our application:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install python-pip python-setuptools
pip install virtualenv
cd /var/www/&amp;lt;app-name&amp;gt;
virtualenv env&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we need to fix some permissions so that uWSGI can read scripts and write the .pyc files:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo usermod -a -G nginx $USER
sudo chown -R $USER:nginx /var/www/&amp;lt;app-name&amp;gt;/
sudo chmod -R g+w /var/www/&amp;lt;app-name&amp;gt;/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/nginx/conf.d/&amp;lt;app-name&amp;gt;.conf&lt;/code&gt; with these contents:&lt;/p&gt;

&lt;pre class=&quot;line-numbers&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;server {
    listen          80;
    server_name     $hostname;
    location /static {
        alias /var/www/&amp;lt;app-name&amp;gt;/static;
    }
    error_page   404              /404.html;
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    location / {
        include         uwsgi_params;
        uwsgi_pass unix:/tmp/uwsgi.sock;
        uwsgi_param UWSGI_PYHOME /var/www/&amp;lt;app-name&amp;gt;/env;
        uwsgi_param UWSGI_CHDIR /var/www/&amp;lt;app-name&amp;gt;/project;
        uwsgi_param UWSGI_MODULE &amp;lt;project-name&amp;gt;.wsgi:application;
    }      
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Don’t forget to swap out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;app-name&amp;gt;&lt;/code&gt; for the name of your app in lines 5, 15, and 16, and use your own project name on line 17.&lt;/p&gt;

&lt;p&gt;In this configuration, nginx will serve files in the static directory, but pass off everything else to uWSGI.&lt;/p&gt;

&lt;p&gt;Now let’s move the default config out of the way, so the new config can take it’s place.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /etc/nginx/conf.d
sudo mv default.conf default.conf.bak&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;OK let’s start things up:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo service uwsgi restart
sudo service nginx restart&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/Screenshot-The-page-is-temporarily-unavailable-Chromium-1.png&quot; alt=&quot;uWSGI error screen&quot; width=&quot;400&quot; height=&quot;391&quot; class=&quot;size-full wp-image-790&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If everything has be setup properly so far, navigating to the IP address should now show a uWSGI error.&lt;/p&gt;

&lt;h2 id=&quot;install-the-django-app&quot;&gt;Install the Django app&lt;/h2&gt;

&lt;p&gt;Now install your Django app into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/app-name/project/&lt;/code&gt; For now, just get a clean copy out of source control and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp&lt;/code&gt; it to the server, or grab it directly from source control while on the server. Later, you should setup a deployment script to automate this, but for now let’s just get the app running.&lt;/p&gt;

&lt;p&gt;In this case, I am keeping my code in a Bitbucket git repository, and I want to pull the code directly from Bitbucket to the server. So on the Bitbucket site, I create a new user with read only permissions on the code repository. Then I create a new ssh key on the server&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t rsa -C &quot;&amp;lt;email address&amp;gt;&quot;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and add it to the Bitbucket account for the new user by uploading the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_rsa.pub&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Now to install git&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install git&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And pulling the source code out of the repository now looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /var/www/&amp;lt;app-name&amp;gt;/project/
git archive --format=tar --remote=ssh://git@bitbucket.org/&amp;lt;username&amp;gt;/&amp;lt;repo name&amp;gt;.git master | tar -xf -&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then fix up any permissions issues again:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo chown -R $USER:nginx /var/www/&amp;lt;app-name&amp;gt;/
sudo chmod -R g+w /var/www/&amp;lt;app-name&amp;gt;/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Install any packages required by your app or it’s dependencies. For example, psycopg2 and lxml respectively depend on the following packages:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install python-dev libpq-dev
sudo apt-get install libxml2-dev libxslt-dev&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now install the project requirements into the virtual environment:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;source /var/www/&amp;lt;app-name&amp;gt;/env/bin/activate
pip install -r /var/www/&amp;lt;app-name&amp;gt;/project/requirements.txt
deactivate&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;OK so now that the Django code files are on the server and the dependencies are satisfied, navigating to the IP address in your browser should show your app. Static files aren’t being served yet and the database isn’t installed, but we can see some progress.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/Screenshot-Search-for-Author-Alcove-Chromium.png&quot; alt=&quot;Application screenshot without static files or database connection.&quot; class=&quot;size-full wp-image-787&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you haven’t got this far, the log files for upstart, nginx, and uwsgi are a good place to start troubleshooting.&lt;/p&gt;

&lt;p&gt;In order to have Nginx serve the static files, they need to be collected to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/&amp;lt;app-name&amp;gt;/static&lt;/code&gt;. In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.py&lt;/code&gt; point your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATIC_ROOT&lt;/code&gt; to that static directory, then verify that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATIC_URL&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATICFILES_DIRS&lt;/code&gt; are properly set up. Get any changes from the repository, then:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;source /var/www/&amp;lt;app-name&amp;gt;/env/bin/activate
cd /var/www/&amp;lt;app-name&amp;gt;/project/ 
python manage.py collectstatic&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Test it out in your browser and you should now see css, javascript, and image files being loaded. However, without the database, your app probably isn’t doing much of anything. So let’s fix that.&lt;/p&gt;

&lt;h2 id=&quot;installing-postgresql&quot;&gt;Installing PostgreSQL&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt-get install postgresql postgresql-contrib&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then setup passwords:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo passwd postgres
su postgres
createuser django --pwprompt
psql create database &amp;lt;dbname&amp;gt; owner django
exit
sudo /etc/init.d/postgresql restart&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now initialize the database:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cd /var/www/&amp;lt;app-name&amp;gt;
source env/bin/activate
cd project
./manage.py syncdb&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you use south for database migrations, run those now.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;./manage.py migrate &amp;lt;name&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Your Django app should now be up and running.&lt;/p&gt;

&lt;h2 id=&quot;closing-remarks&quot;&gt;Closing remarks&lt;/h2&gt;

&lt;p&gt;Now that the server is ready, &lt;a href=&quot;http://grokcode.com/792/deploy-with-git-push/&quot;&gt;setup one click deploys&lt;/a&gt; to speed up your code – test – deploy cycle.&lt;/p&gt;

&lt;p&gt;Support the creation of tutorials like this one by using the following links to &lt;a href=&quot;http://www.linode.com/?r=e748550930d0390f86a171bc3465ce8bc0e211a6&quot;&gt;purchase Linode hosting&lt;/a&gt; or &lt;a href=&quot;http://www.amazon.com/?tag=gc-cred-20&quot;&gt;shop at Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A big thanks to &lt;a href=&quot;http://blog.kramerapps.com/post/22551999777/flask-uwsgi-nginx-ubuntu&quot;&gt;Conrad Kramer&lt;/a&gt;, &lt;a href=&quot;http://www.collectivedisorder.com/ubuntu&quot;&gt;Collective Disorder&lt;/a&gt;, and &lt;a href=&quot;http://www.westphahl.net/blog/2010/4/8/running-django-nginx-and-uwsgi/&quot;&gt;Simon Westphahl&lt;/a&gt; for guiding the creation of this tutorial.&lt;/p&gt;
</description>
        <pubDate>Wed, 25 Jul 2012 21:45:12 +0000</pubDate>
        <link>http://www.grokcode.com/784/how-to-setup-a-linux-nginx-uwsgi-python-django-server/</link>
        <guid isPermaLink="true">http://www.grokcode.com/784/how-to-setup-a-linux-nginx-uwsgi-python-django-server/</guid>
        
        <category>django</category>
        
        <category>linux</category>
        
        <category>nginx</category>
        
        <category>postgres</category>
        
        <category>precise pangolin</category>
        
        <category>production server</category>
        
        <category>python</category>
        
        <category>ubuntu</category>
        
        <category>uwsgi</category>
        
        
        <category>Tips &amp;amp; Tutorials</category>
        
      </item>
    
      <item>
        <title>Dear Python, Why Are You So Ugly?</title>
        <description>&lt;p&gt;Dear Python, something has been bothering me for a while. Its just that, well, ummm…you’re kind of ugly. Look, you are beautiful inside: Python is a beautiful language and the Python community is open and welcoming. But Python resources are ugly enough to affect usability and adoption. This is damaging to the community.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Documentation and tutorials are often difficult to navigate. Products built with Python don’t put any thought into design. Blogging software written in Python helps create more ugly blogs about Python. Pythonistas just don’t care about presentation.&lt;/p&gt;

&lt;p&gt;Let’s compare the usability and design of some different Python and Ruby sites. Ruby is a good reference point since it occupies a similar niche in the programming language ecosystem and is roughly the same age.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;http://www.learnpython.org/&quot;&gt;&lt;img src=&quot;/assets/img/learn-python-tutorial.png&quot; alt=&quot;Python Interactive Tutorial&quot; class=&quot;left half size-full wp-image-756&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://tryruby.org/&quot;&gt;&lt;img src=&quot;/assets/img/ruby-tutorial-Code-School-TryRuby.png&quot; alt=&quot;Ruby Interactive Tutorial&quot; class=&quot;left half last size-full wp-image-754&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Above are online interactive tutorials for learning Python vs. Ruby. Learnpython.org isn’t ugly exactly, it’s just that Try Ruby is so much more polished. It’s obvious that a professional designer had a hand in Try Ruby, whereas the design of learnpython.org was thrown together by a programmer who later slapped a few ads on it and called it done. Which tutorial would you rather use?&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;&lt;img src=&quot;/assets/img/django-framework2.png&quot; alt=&quot;Django web framework&quot; class=&quot;left size-full wp-image-760&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://rubyonrails.org/&quot;&gt;&lt;img src=&quot;/assets/img/ruby-on-rails-framework1.png&quot; alt=&quot;Ruby on Rails web framework&quot; class=&quot;left last size-full wp-image-761&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Here are the homepages for Django and Ruby on Rails, popular web frameworks. Django’s homepage is poorly organized. The entire page is just a mess of links with no clear emphasis on anything. In contrast, the Ruby on Rails homepage does a good job of introducing people to Rails and pointing them to different areas of the site.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;http://www.holovaty.com/&quot;&gt;&lt;img src=&quot;/assets/img/Adrian-Holovaty.png&quot; alt=&quot;Adrian Holovaty&apos;s Homepage&quot; class=&quot;left size-full wp-image-763&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://david.heinemeierhansson.com/&quot;&gt;&lt;img src=&quot;/assets/img/David-Heinemeier-Hansson.png&quot; alt=&quot;David Heinemeier Hansson&apos;s Homepage&quot; class=&quot;left last size-full wp-image-762&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Homepage of Django co-creator Adrian Holovaty vs. Ruby on Rails creator David Heinemeier Hansson. Both are well organized, but it is obvious at a glance who places importance on professional design and who doesn’t.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;https://developers.google.com/appengine/&quot;&gt;&lt;img src=&quot;/assets/img/Google-App-Engine.png&quot; alt=&quot;Google App Engine hosting&quot; class=&quot;left size-full wp-image-769&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://www.heroku.com/&quot;&gt;&lt;img src=&quot;/assets/img/Heroku-Cloud-Application-Platform.png&quot; alt=&quot;Heroku cloud hosting&quot; class=&quot;left last size-full wp-image-768&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Cloud hosting: Google App Engine vs. Heroku. Once again the Ruby side is sexier.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;http://www.blogofile.com/&quot;&gt;&lt;img src=&quot;/assets/img/Blogofile.png&quot; alt=&quot;Blogofile blogging software&quot; class=&quot;left size-full wp-image-773&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://jekyllrb.com/&quot;&gt;&lt;img src=&quot;/assets/img/jekyll.png&quot; alt=&quot;Jekyll blogging software&quot; class=&quot;left last size-full wp-image-770&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;http://tinkerer.bitbucket.org/&quot;&gt;&lt;img src=&quot;/assets/img/tinkerer.png&quot; alt=&quot;Tinkerer Python blogging software&quot; class=&quot;left size-full wp-image-772&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://octopress.org/&quot;&gt;&lt;img src=&quot;/assets/img/Octopress.png&quot; alt=&quot;Octopress Ruby blogging software&quot; width=&quot;322&quot; height=&quot;240&quot; class=&quot;left last size-full wp-image-771&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Python blogging software vs Ruby blogging software. The Python blog designs are uninspiring and unpolished, while the Ruby designs are striking. And who doesn’t love octopodes?&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;http://www.egenix.com/&quot;&gt;&lt;img src=&quot;/assets/img/egenix1.png&quot; alt=&quot;eGenix Python consulting&quot; class=&quot;left size-full wp-image-778&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://norbauerinc.com/&quot;&gt;&lt;img src=&quot;/assets/img/ruby-on-rails-consulting.png&quot; alt=&quot;Norbauerinc ruby consulting&quot; class=&quot;left last size-full wp-image-774&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;http://www.tummy.com/Services/Consulting/python.html&quot;&gt;&lt;img src=&quot;/assets/img/tummy.png&quot; alt=&quot;Tummy Python consulting&quot; class=&quot;left size-full wp-image-776&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://littlelines.com/&quot;&gt;&lt;img src=&quot;/assets/img/littlelines.png&quot; alt=&quot;Littlelines Ruby consulting&quot; class=&quot;left last size-full wp-image-775&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Some top Google search results for “python consulting” vs. “ruby consulting”. The Python results look painfully outdated.&lt;/p&gt;

&lt;p&gt;Maybe Ruby has an advantage because it is more tightly focused on web programming? It seems likely that consultants for web apps would have nicer web pages than consultants working on, say, scientific problems. To eliminate any unfair advantage, below are some top Google search results for “django consulting” vs. “ruby on rails consulting”.&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;clearfix&quot;&gt;&lt;a href=&quot;http://www.fezconsulting.com/&quot;&gt;&lt;img src=&quot;/assets/img/fez-consulting.png&quot; alt=&quot;Fez Django consulting&quot; class=&quot;left size-full wp-image-780&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://integrumtech.com/&quot;&gt;&lt;img src=&quot;/assets/img/scrum-and-agile-coaching.png&quot; alt=&quot;Scrum and agile rails consulting&quot; class=&quot;left last size-full wp-image-781&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;It doesn’t matter. Python is still the ugly duckling.&lt;/p&gt;

&lt;p&gt;This wasn’t a scientific comparison – somebody could try show the opposite conclusion by cherry-picking a different set of sites – but I think the screenshots I used are pretty representative. I also think that most people who have spent a little bit of time in both communities will agree that Python sites &lt;em&gt;are&lt;/em&gt; ugly compared to Ruby sites.&lt;/p&gt;

&lt;h2 id=&quot;why-does-this-happen&quot;&gt;Why does this happen?&lt;/h2&gt;

&lt;p&gt;Either Pythonistas aren’t pairing with designers as often as Rubyists, Pythonistas have less design talent, or Pythonistas simply don’t care enough about design to spend the time to do it right. I’m not really sure which is the case.&lt;/p&gt;

&lt;h2 id=&quot;does-it-matter&quot;&gt;Does it matter?&lt;/h2&gt;

&lt;p&gt;Yes! This stuff matters.&lt;/p&gt;

&lt;p&gt;It’s not just that Python sites are ugly (even though they are). It’s that the uglyness makes sites hard to navigate and hard to use. It’s that nobody is inspired by uglyness and nobody wants to use ugly products when there are better options. Nobody wants to hire someone who builds ugly web apps. It is unprofessional and sloppy to be so ugly.&lt;/p&gt;

&lt;p&gt;This damages the Python community.&lt;/p&gt;

&lt;h2 id=&quot;how-can-we-fix-it&quot;&gt;How can we fix it?&lt;/h2&gt;

&lt;p&gt;If you are a Python coder (or any type of coder who doesn’t fully grok design), spend some time learning basic design principles. Try &lt;a href=&quot;http://www.amazon.com/gp/product/1119998956/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=grok-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1119998956&quot;&gt;Design for Hackers&lt;/a&gt;, &lt;a href=&quot;http://bootstrappingdesign.com/&quot;&gt;Bootstrapping Design&lt;/a&gt;, or &lt;a href=&quot;http://sachagreif.com/ebook/&quot;&gt;Step by Step UI Design&lt;/a&gt;. [Edit: Also check out this lightening talk on &lt;a href=&quot;http://t.co/nS6vG0cj&quot;&gt;attracting designers to your project&lt;/a&gt;.] Then make friends with designers and know when to call them.&lt;/p&gt;
</description>
        <pubDate>Thu, 26 Apr 2012 12:04:02 +0000</pubDate>
        <link>http://www.grokcode.com/746/dear-python-why-are-you-so-ugly/</link>
        <guid isPermaLink="true">http://www.grokcode.com/746/dear-python-why-are-you-so-ugly/</guid>
        
        <category>django</category>
        
        <category>python</category>
        
        <category>ruby</category>
        
        <category>ruby on rails</category>
        
        <category>UX design</category>
        
        <category>web design</category>
        
        
        <category>Programming Languages</category>
        
      </item>
    
      <item>
        <title>Startup Sheep Vs. Non-Startup Goats (Or Transitioning From Coder to Founder)</title>
        <description>&lt;p&gt;There is some &lt;a href=&quot;http://www.eis.mdx.ac.uk/research/PhDArea/saeed/&quot;&gt;famous research&lt;/a&gt;, by Saeed Dehnadi and Richard Bornat, about “programming sheep and non-programming goats.” The gist is that educators find that there are two populations of students, those who can program, and those who can’t. Each population has it’s own independent bell curve. This “double hump” persists despite variations in programming language, application type, IDE, and student motivation.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;So, there are natural born programmers. And there are natural born non-programmers.&lt;/p&gt;

&lt;p&gt;Most natural born programmers notice this at some point (consciously or not), but often the conclusion they draw from it is overly general. Coders who can code may naively expect to found startups that, ermm, start. Or they may expect to be naturally good at other things that are only tangentially related to coding.&lt;/p&gt;

&lt;p&gt;Lets take the example of a founder working on a software startup. Our founder is a programmer, at the top end of the programming bell curve. He is a really smart guy, he has done some reading on startups, but he hasn’t launched anything before, and he is entirely unaware that he has developed a severe case of &lt;a href=&quot;http://steve-yegge.blogspot.com/2009_04_01_archive.html&quot;&gt;shit’s easy syndrome&lt;/a&gt; regarding the non-programming tasks that need to get done before launch.&lt;/p&gt;

&lt;p&gt;As programmers, we learn to be &lt;a href=&quot;http://grokcode.com/722/be-a-paranoid-pessimistic-programmer/&quot;&gt;obsesively paranoid with our requirements analysis, timelines, and code&lt;/a&gt; otherwise the project will run off the rails. But we don’t apply that same rigor for business tasks, because well, that shit’s easy, right?&lt;/p&gt;

&lt;h2 id=&quot;turning-a-project-into-a-product--that-shits-not-easy&quot;&gt;Turning a project into a product – that shit’s not easy&lt;/h2&gt;

&lt;p&gt;Packaging a programming project into a product that can be sold is a complex process. Assuming that the programming side is a beautiful finely-tuned instrument ticking along perfectly, there are still a behemothic number of things that need to be done to get it into the hands of paying customers, and each decision that needs to be made opens up into a fractally expanding rabbit hole leading to an infinite number of other decisions.&lt;/p&gt;

&lt;p&gt;It starts off innocently enough: OK I need a website to sell this thing. How about a site with a free theme plus some marketing copy explaining the product tied into a simple shopping cart so customers can buy the product.&lt;/p&gt;

&lt;p&gt;Which quickly turns into: How about a site with a free theme (which themes are optimized for SEO, which are optimized for conversions, how about some A/B testing, hey why is my site full of spam&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; plus some marketing copy explaining the product (what kind of copy sells, OK “benefits” are better than “features”, but now what are the product’s benefits…) tied into a simple shopping cart (secure digital delivery, payment processing, cart software, should I write some of this myself, will this stack work with an affiliate program…) so customers (who exactly are my customers, what “tribe” are they in, how do I find them, should I try advertising, where…) can buy the product. And spirals out of control from there.&lt;/p&gt;

&lt;p&gt;It’s becoming obvious how “hello world hooked up to a random number generator” requires a super-smart, full-time guy doing the business tasks.&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Meanwhile, all of the startup guru expert guys are in my head screaming, “JUST LAUNCH! Iterate later,” but at this point I have lost all sense of what is absolutely necessary to prevent everything from blowing up in my face, what will just make me look stupid if it breaks, and what I can launch without and add later.&lt;/p&gt;

&lt;p&gt;This launch paralysis is a problem for startups on down to micro-startups and coders that are just trying to make a bit of side money off of a weekend project. Programmers are probably more susceptible to launch paralysis because we are used to just naturally ‘getting it’ (remember, we have our own bell curve), and when confronted with mushy tasks like research or copywriting that don’t require the rigor of programming, we tend to fall into the shit’s easy trap.&lt;/p&gt;

&lt;h2 id=&quot;a-tiny-example&quot;&gt;A Tiny Example&lt;/h2&gt;

&lt;p&gt;My idea was to launch a weekend project, and use the experience to identify problems and iron out glitches in my workflow before tackling something bigger. I uncovered more than just glitches. Everything took drastically longer than expected, and some things I didn’t plan for at all.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/time-to-launch-actual-vs-estimated.png&quot; alt=&quot;Time to Launch: Planned vs. Actual&quot; width=&quot;615&quot; height=&quot;375&quot; class=&quot;aligncenter size-full wp-image-743&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The graph above is data from a tiny project that makes some head-smackingly simple changes to Amazon links so that WordPress blogs earn worldwide commissions instead of just US commissions. I assumed it would be easy to sell since the purchase is so easy to justify – once a blog has a certain threshold of earnings from amazon.com, the plugin is pretty much guaranteed to pay for itself. If you are interested, &lt;del datetime=&quot;2014-07-31T16:34:08+00:00&quot;&gt;click here to see how the plugin increases earnings&lt;/del&gt;. Now defunct.&lt;/p&gt;

&lt;p&gt;This wasn’t a startup. Calling it a micro-startup would be generous. It’s a WordPress plugin. We’re talking 700 lines of code, tops. But it still took 5 months to launch it. I was only working on it part time, but still, the number of hours spent getting ready to launch such a simple project is staggering.&lt;/p&gt;

&lt;p&gt;Much of that time was spent researching different tools, not actually integrating them. Luckily, most of that knowledge will transfer to future projects. Now I have a standard toolchain that handles design, digital delivery, payment processing, A/B testing, etc. I will be able to put this type of site together much faster in the future.&lt;/p&gt;

&lt;h2 id=&quot;so-it-finally-launched-sheep-or-goat&quot;&gt;So, it finally launched. Sheep or goat?&lt;/h2&gt;

&lt;p&gt;Sales so far have been lower than expected, and certainly less than spectacular. At times it feels a lot like a big goat failure. But there are still some sheep to be found. After &lt;a href=&quot;http://www.jasonshen.com/2011/getting-your-groove-back/&quot;&gt;stepping back for a bit of perspective&lt;/a&gt;, I remember that this little failure created some big wins. First, all of that research gave me a standard toolchain for launching mini software startups (&lt;a href=&quot;http://grokcode.com/732/launch-faster-the-tools-to-do-it-without-looking-like-a-fool/&quot;&gt;see my toolchain here&lt;/a&gt;). Next time things will move much faster. Second, even though the plugin hasn’t earned much through sales, having it installed on my own sites has given a nice bump to passive income.&lt;/p&gt;

&lt;p&gt;The launch isn’t really the end of the story. After launch comes a whole new rabbit hole of customer acquisition and iterating the product. It’s a bit too early to make a success or failure call. Everyone says that an overnight success takes years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; This project now defunct. I still use the plugin on my own sites where it has increased earnings a fair amount, but the support and website maintenance overhead associated with selling the plugin to others was not worth my time.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;http://wpmu.org/why-you-should-never-search-for-free-wordpress-themes-in-google-or-anywhere-else/&quot;&gt;Why you should never search for free WordPress themes&lt;/a&gt; &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;This is in reference to &lt;a href=&quot;http://www.kalzumeus.com/&quot;&gt;Patrick McKenzie&lt;/a&gt;‘s Bingo Card Creator software, which is technically pretty unsophisticated itself, but is supported by complex mechanisms for A/B testing, scalable content generation, conversion tracking, and various other types of optimization. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Thu, 29 Mar 2012 12:23:32 +0000</pubDate>
        <link>http://www.grokcode.com/741/startup-sheep-vs-non-startup-goats-or-transitioning-from-coder-to-founder/</link>
        <guid isPermaLink="true">http://www.grokcode.com/741/startup-sheep-vs-non-startup-goats-or-transitioning-from-coder-to-founder/</guid>
        
        <category>coder</category>
        
        <category>founder</category>
        
        <category>launching</category>
        
        <category>programmer</category>
        
        <category>startups</category>
        
        
        <category>Career</category>
        
      </item>
    
      <item>
        <title>Launching Downloadable Products Quickly</title>
        <description>&lt;p&gt;My intent is to give recommendations that will be of use to other programmers who are trying to quickly turn a side project into a professional product that they can sell.&lt;!--more--&gt;&lt;/p&gt;

&lt;p&gt;Assuming you already have a working side project or product (no small feat in itself, but not difficult for a coder to put together), the biggest missing piece is a website for getting that project into the hands of customers. It’s easy to underestimate how much work it takes to do this part well if you have never done it before – design, conversion optimization, payment processing, secure digital product delivery, analytics, affiliate programs…&lt;/p&gt;

&lt;p&gt;Researching this stuff is a &lt;em&gt;huge&lt;/em&gt; time suck. Here are the tools I recommend for getting a professional site done quickly. This setup has no monthly fees and low per transaction fees, but does require some glue code to get working.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://themeforest.net?ref=grokcode&quot;&gt;&lt;img src=&quot;/assets/img/themeforest.jpg&quot; alt=&quot;themeforest screenshot&quot; width=&quot;360&quot; height=&quot;209&quot; class=&quot;alignleft size-full&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://themeforest.net?ref=grokcode&quot;&gt;ThemeForest&lt;/a&gt; is an online marketplace for site templates and themes.&lt;/p&gt;

&lt;p&gt;Even if you plan on moving to a custom design later, using a pre-built theme at first will save time and let you launch sooner. Plus the initial feedback from customers who bought the product on the themed site will help to guide the custom design.&lt;/p&gt;

&lt;p&gt;I prefer ThemeForest because all themes are manually reviewed by the ThemeForest staff before being added to the store – this means no secret spam links or malicious code embedded in your themes. (This is a huge problem with free themes.)&lt;/p&gt;

&lt;p&gt;Professional looking themes typically sell for between $8 and $15, which is a pittance for the time it will save if you were to code it yourself. &lt;a href=&quot;http://themeforest.net?ref=grokcode&quot;&gt;Browse themes here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A stock theme will still need some customization. For custom graphics, try &lt;a href=&quot;http://fiverr.com/&quot;&gt;Fivvr&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I add a few extra pages to the theme to handle checkout: a payment page, a verify purchase page, and a thank-you or payment received page. These pages contain a bit of custom glue code to use my preferred payment processor, digital delivery method, and affiliate program tracker. The other benefit of handling these pages myself, instead of passing control off to an external cart or payment processor, is that it allows more control over the checkout process. This is valuable for gathering data about cart abandonment rates, providing opportunities for upselling, and optimizing these pages for conversion.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://stripe.com/&quot;&gt;&lt;img src=&quot;/assets/img/stripe.jpg&quot; alt=&quot;stripe screenshot&quot; width=&quot;360&quot; height=&quot;209&quot; class=&quot;alignleft size-full&quot; /&gt;&lt;/a&gt;Previously, writing or hosting those pages yourself would be a huge security risk and a PCI compliance nightmare, but with &lt;a href=&quot;http://stripe.com&quot;&gt;Stripe&lt;/a&gt; to do the heavy lifting it’s a secure option.&lt;/p&gt;

&lt;p&gt;Stripe makes it easy to accept credit cards on the web.&lt;/p&gt;

&lt;p&gt;Payments take place through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stripe.js&lt;/code&gt;, so your customers never leave your site during the checkout process, and you skip most PCI requirements since the transaction itself happens on Stripe’s servers, not yours.&lt;/p&gt;

&lt;p&gt;The API is a joy to work with, and there are Stripe employees hanging out in the Campfire room at all hours of the day and night who can answer integration questions.&lt;/p&gt;

&lt;p&gt;Cost is 2.9% plus $0.30 per successful charge. There are no hidden fees. Earnings are transferred to your account an a 7 day rolling basis.&lt;/p&gt;

&lt;p&gt;Unlike most forms of accepting online payments, the sign up process is painless, and you can start with the test API right away. &lt;a href=&quot;https://stripe.com/&quot;&gt;Sign up for Stripe.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://fetchapp.com/&quot;&gt;&lt;img src=&quot;/assets/img/fetch.jpg&quot; alt=&quot;fetch screenshot&quot; width=&quot;360&quot; height=&quot;209&quot; class=&quot;alignleft size-full&quot; /&gt;&lt;/a&gt;Once payment has been processed, &lt;a href=&quot;http://fetchapp.com/&quot;&gt;FetchApp&lt;/a&gt; provides secure delivery of digital files.&lt;/p&gt;

&lt;p&gt;Each purchase triggers the creation of a unique download link. You can configure the length of time the link is valid and the number of times it can be used.&lt;/p&gt;

&lt;p&gt;There is a simple online interface and an equally slick API to manage products, orders, and customers. The feature set includes the ability to send free trials and notify customers when a new version of a product is available.&lt;/p&gt;

&lt;p&gt;FetchApp integrates with a number of shopping carts and payment providers out of the box, but my setup uses the API.&lt;/p&gt;

&lt;p&gt;Pricing is based on amount of storage required, not number of sales. The free plan covers up to 1MB. &lt;a href=&quot;http://app.fetchapp.com/signup?ref=r5bf&quot;&gt;FetchApp sign up page.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above services mixed with some good marketing copy will get you up and running, but you will certainly want to set up a few extras like analytics, A/B testing, and possibly an affiliate program.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.google.com/analytics/&quot;&gt;Google analytics&lt;/a&gt; is the standard choice for analytics – it is easy to setup and provides tons of useful information.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.google.com/websiteoptimizer&quot;&gt;&lt;img src=&quot;/assets/img/google-website-optimizer.jpg&quot; alt=&quot;google website optimizer screenshot&quot; width=&quot;360&quot; height=&quot;209&quot; class=&quot;alignleft size-full&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;http://www.google.com/websiteoptimizer&quot;&gt;Google Website Optimizer&lt;/a&gt; provides A/B and Multivariate Testing.&lt;/p&gt;

&lt;p&gt;There are a number of different options for this type of testing. I prefer GWO because it is easy to setup and the reports are simple to understand.&lt;/p&gt;

&lt;p&gt;Another avenue for increasing sales is to set up an affiliate program, giving people who promote your product a cut of the sales. Companies like &lt;a href=&quot;http://www.cj.com/&quot;&gt;Commission Junction&lt;/a&gt; and &lt;a href=&quot;http://www.clickbank.com/index.html&quot;&gt;ClickBank&lt;/a&gt; will handle this – they help affiliates find your program and track their sales.&lt;/p&gt;

&lt;p&gt;However I don’t recommend either of those programs. Their fee structures are too complicated, and affiliates are often hit with hidden fees. Also, they use “hop links” to track purchases. These “hop links” don’t provide SEO value and look suspicious to customers (a typical link will look something like http://chdxsld.com/kdkd2002kdk before the customer is redirected to your site).&lt;/p&gt;

&lt;p&gt;A self-hosted affiliate program will avoid those problems – you can customize the payout percent and affiliate links will look something like http://example.com/ref=ID, but the drawback is you need to publicize the program yourself and handle the affiliate payments.&lt;/p&gt;

&lt;p&gt;I have yet to find a good affiliate solution, self-hosted or otherwise. Anyone have recommendations?&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.freshbooks.com/&quot;&gt;&lt;img src=&quot;/assets/img/freshbooks.jpg&quot; alt=&quot;freshbooks screenshot&quot; width=&quot;360&quot; height=&quot;209&quot; class=&quot;alignleft size-full&quot; /&gt;&lt;/a&gt;Bonus recommendation: if you intend to support a fledgling startup with contract work on the side, or if charging for support is part of your revenue model, &lt;a href=&quot;http://www.freshbooks.com/&quot;&gt;Freshbooks&lt;/a&gt; is the best option I’ve seen for creating professional invoices.&lt;/p&gt;

&lt;p&gt;The Freshbooks web app is an absolute pleasure to work with.&lt;/p&gt;

&lt;p&gt;Pricing is based on the number of clients, with up to 3 clients on the free plan. &lt;a href=&quot;https://grokcode.freshbooks.com/refer/www&quot;&gt;Use this invite to sign up a free 30 day trial of Freshbooks.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see this software stack in action at &lt;del datetime=&quot;2014-08-01T16:58:45+00:00&quot;&gt;Geotargeter for WordPress – a plugin for Amazon Affiliates&lt;/del&gt;. &lt;strong&gt;Edit:&lt;/strong&gt; Now defunct.&lt;/p&gt;
</description>
        <pubDate>Tue, 27 Mar 2012 15:35:33 +0000</pubDate>
        <link>http://www.grokcode.com/732/launch-faster-the-tools-to-do-it-without-looking-like-a-fool/</link>
        <guid isPermaLink="true">http://www.grokcode.com/732/launch-faster-the-tools-to-do-it-without-looking-like-a-fool/</guid>
        
        <category>a/b testing</category>
        
        <category>affiliate programs</category>
        
        <category>fetchapp</category>
        
        <category>fivvr</category>
        
        <category>freshbooks</category>
        
        <category>launching</category>
        
        <category>sales</category>
        
        <category>startups</category>
        
        <category>stripe</category>
        
        <category>themeforest</category>
        
        
        <category>Books &amp;amp; Tools</category>
        
      </item>
    
  </channel>
</rss>
