-
Python: "ignored" context manager
There was a recent fantastic addition to Python 3.4 by Raymond Hettinger:
contextlib.ignored. It’s a context manager which lets you shorten the following often-occurring pattern:try: os.remove('i_probably_do_not_exist.txt') except OSError: passAnd turn it into this:
with ignored(OSError): os.remove('i_probably_do_not_exist')Much shorted and prettier. But, as probably most of engineers, you have to use older version of python in production. That’s where this little chunk of code comes in. Create a little
compat(as in “compatibility”) library and add following function:import contextlib @contextlib.contextmanager def ignored(*exceptions): try: yield except exceptions: passBeautiful!
UPDATE: As Andy pointed out in comments, this context manager has been renamed to
contextlib.suppress(https://bugs.python.org/issue19266). -
Elegant Mutt setup for use with Gmail
I have been using Mutt for a while now. Wouldn’t say that it saves my time, but nor does it extend the amount of time I spend reading email. For me, the best part about Mutt is that it lets me use text editor of my choice - Vim. Everything else - keyboard shortcuts, minimalist design, and simplicity - already exists in Gmail.
I found configuration below to work really well for my needs: all of the important for me Gmail features are translated. Here’s my
.muttrcfile:bind editor <space> noop set alias_file = '~/.mutt/aliases.txt' set copy = no set display_filter = '$HOME/.mutt/aliases.sh' set edit_headers set editor = "vim +/^$ ++1" set folder = "imaps://imap.gmail.com/" set hostname = "gmail.com" set imap_check_subscribed set imap_pass = "$PASSWORD" set imap_user = "$USERNAME" set mail_check = 5 set move = no set postponed = "+[Gmail]/Drafts" set spoolfile = "+INBOX" set text_flowed = yes unset imap_passive unset record # Gmail-style keyboard shortcuts macro index ga "<change-folder>=[Gmail]/All Mail<enter>" "Go to all mail" macro index gd "<change-folder>=[Gmail]/Drafts<enter>" "Go to drafts" macro index gi "<change-folder>=INBOX<enter>" "Go to inbox" macro index gs "<change-folder>=[Gmail]/Starred<enter>" "Go to starred messages" macro index gt "<change-folder>=[Gmail]/Trash<enter>" "Go to trash" macro index,pager d "<save-message>=[Gmail]/Trash<enter><enter>" "Trash" macro index,pager y "<save-message>=[Gmail]/All Mail<enter><enter>" "Archive" source $alias_fileIt is quite self-explanatory, and includes such nice features as:
- Automatically adding addresses from read emails to address book (see below).
- Using vim as a text editor, with an ability to edit message headers/recipients from within vim.
- Ability to access all the default Gmail folders: All mail, Drafts, Inbox, Starred, Trash.
- Key bindings to delete and archive messages bound to
dandyrespectfully (I am a huge fun of a zero-mail inbox).
You might also want to have your password encrypted by GPG as opposed to leaving it in plain text in your
.muttrcfile. You can read how to do this here: Using Mutt with GPG.As you may have noticed,
.muttrcabove setsdisplay_filterto$HOME/.mutt/aliases.sh. This script is being executed every time you read an email, and it collects email address to$HOME/.mutt/aliases.txt. Contents of thealiases.share below:#!/bin/sh MESSAGE=$(cat) NEWALIAS=$(echo "${MESSAGE}" | grep ^"From: " | sed s/[\,\"\']//g | awk '{$1=""; if (NF == 3) {print "alias" $0;} else if (NF == 2) {print "alias" $0 $0;} else if (NF > 3) {print "alias", tolower($(NF-1))"-"tolower($2) $0;}}') if grep -Fxq "$NEWALIAS" $HOME/.mutt/aliases.txt; then : else echo "$NEWALIAS" >> $HOME/.mutt/aliases.txt fi echo "${MESSAGE}"Source: W. Caleb McDaniel.
This script will create
aliases.txtfile containing email addresses for search and auto completion of email-addresses. -
Using Mutt with GPG
Mutt is a great command line email client, but it does not offer a built-in way to store passwords. But that’s where GPG comes in. A while back I wrote an article on how to use GPG to store your passwords: GPG Usage, this is a more practical note about using GPG to store your passwords for mutt. This note implies that you already have installed and configured GPG (which you can learn how to do in above linked article).
First you will have to record a password to a GPG file. Replace
$PASSWORDwith your password and$ACCOUNTwith a desired account alias. You probably want to prefix this command with a space, to avoid writing your password to a history file.echo '$PASSWORD' | gpg --use-agent -e > ~/.passwd/$ACCOUNT.gpgNext, open your
~/.muttrcfile and add following line:set imap_pass = "`gpg --use-agent --quiet --batch -d ~/.passwd/$ACCOUNT.gpg`"Again, replace
$ACCOUNTwith the same account alias you specified earlier. Now you don’t have to re-enter your password every time you start Mutt. -
Navigate files easily with ctrlp.vim
Few years ago I used a lightweight IDE called “Sublime Text 2”. And one of the most compelling features of it was an ability to switch between files by hitting
Ctrl+Pand typing in a part of the file name.This is exactly what
ctrlp.vimimplements in vim. Usage is extremely simple: install the plugin, hitCtrl+P, type a part of the file name, select a desired file and hitEnter. You can use arrow keys orCtrl+JandCtrlKto navigate between suggested file list.
Source: https://github.com/kien/ctrlp.vim.
-
Making Django and Lettuce play nice together
Lettuce is a great BDD tool which allows you to parse expressions written via Gherkin syntax in python. However the documentation is not very comprehensive, and at the moment current version (0.2.19) has some issues integrating with the latest Django (1.6.1 at the moment of writing). Without further ado, I’ll get to a comprehensive tutorial.
Let’s assume you are using
pipandvirtualenvfor the dependency control, and you already have a working project configured. Your project is called “myproject”, and the only app you have within your project is called “polls”.Setup
First, you have to install
lettucelibrary. At the moment of writing, current released version (0.2.19) has an error integrating with Django, so we’ll install current development version. Releases 0.2.20 and up should include the fix, sopip install lettucewould be better if the version is out.pip install -e \ git://github.com/gabrielfalcao/lettuce@cccc397#egg=lettuce-master pip install django-nose splinter pip freeze > requirements.txtFirst line downloads lettuce package from the github repository and installs missing dependencies. You can replace
cccc397with the current commit. Technically commit can be omitted, but we don’t want to have an unstable ever-changing branch in ourrequirements.txt. I also addeddjango-nosesince nose assertions come in handy while writing Lettuce steps, as well assplinter, which is a great tool for testing web application.Add Lettuce to the
INSTALLED_APPSin yourmyproject/settings.py:INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.admindocs', # ... third party apps ... 'lettuce.django', 'myproject', 'polls', )You also have to explicitly specify the apps you want to use with lettuce:
LETTUCE_APPS = ( 'polls', )By default, lettuce will run its’ tests against your default database. But we want to use test database for that, so we have to add few more settings:
LETTUCE_TEST_SERVER = 'lettuce.django.server.DjangoServer' LETTUCE_SERVER_PORT = 9000Where
LETTUCE_TEST_SERVERis a subclass of Django’sLiveTestServerCase- a class which runs a test server for you andLETTUCE_SERVER_PORTis different from port 8000 so you won’t have issues running the development server viapython manage.py runserverat the same time as running Lettuce tests.You also have to create a
featuresdirectories inside the apps you want to test with Lettuce:/myproject /myproject __init__.py settings.py urls.py wsgi.py /polls /features /steps __init__.py polls_list.py polls_list.feature __init__.py models.py tests.py views.py manage.py requirements.txt terrain.pyLettuce has its’ own settings file called
terrain.py. It has to be in the same directory as amanage.py:from django.core.management import call_command from django.conf import settings from lettuce import before, after, world from splinter.browser import Browser @before.each_scenario def flush_database(scenario): call_command('flush', interactive=False, verbosity=0) @before.each_scenario def prepare_browser(scenario): world.browser = Browser() @after.each_scenario def destroy_browser(scenario): world.browser.quit()This code flushes the database before each scenario, as well as prepares and destroys the
splinterbrowser.Writing the features
Feature files support standard Gherkin syntax, let’s write one right now in
polls/features/polls_list.feature:Feature: Polls list Scenario: Polls list without any polls When I access the "polls list" url Then I see a text "We didn't find any polls!" Scenario: Polls list with one poll Given a poll with the title "Hello world" When I access the "polls list" url Then I see a text "Hello world" And I do not see a text "We didn't find any polls!"Now describe the steps in
polls/features/steps/polls_list.py:from django.core.urlresolvers import reverse from lettuce import step, world from lettuce.django import django_url from nose.tools import assert_in, assert_not_in from polls.models import Poll PAGES = { "polls list": "polls:list" } @step(r'access the "(.*)" url') def access_the_url(step, name): world.browser.visit(django_url(reverse(PAGES[name]))) @step(r'see a text "(.*)"') def see_a_text(step, text): assert_in(text, world.browser.html) @step(r'a poll with the title "(.*)"') def create_a_poll_with_the_title(step, title): poll = Poll.objects.create(title=title) polls.save() @step(r'do not see a text "(.*)"') def do_not_see_a_text(step, text): assert_not_in(text, world.browser.html)Running the tests
Now, you can run
python manage.py harvest --test-serverto run the tests you just wrote:$ python manage.py harvest --test-server Creating test database for alias 'default'... Django's builtin server is running at 0.0.0.0:9000 Feature: Polls list Scenario: Polls list without any polls When I access the "polls list" url Then I see a text "We didn't find any polls!" Scenario: Polls list with one poll Given a poll with the title "Hello world" When I access the "polls list" url Then I see a text "Hello world" And I do not see a text "We didn't find any polls!" 1 feature (1 passed) 2 scenarios (2 passed) 6 steps (6 passed) Destroying test database for alias 'default'...Don’t forget the
--test-serverswitch - otherwise Lettuce will run tests against your default database.Sources
You can find some more details on Lettuce and Django integration here: Web development fun with Lettuce and Django.
Update
Rather than using
--test-serverswitch, it’s easier and safer to set a flag in yoursettings.py(suggested by Michel Sabchuk):LETTUCE_USE_TEST_DATABASE = TrueThis way you won’t end up accidentally erasing your production database after forgetting to add
--test-serverflag.