Python Code Formatting Standards

Python Coding Guidelines

These guidelines should be followed when writing Python code. Patches and additions to any code base will be checked for adherence to these guidelines. If code is in violation, you will be asked to reformat it.

PEP-8

For most styling practices, follow the official PEP-8 Python style guide.

Exceptions to PEP-8

Line length

We use a maximum 100 character line length, or 99 characters + a newline.

(The only exception to this rule is configuring URL routing, e.g. in Django or Flask. In this case, readability of URL routes is significantly increased if each urlpattern is on a single line.)

Continuing long lines

This should be less of a problem with a 100 character max, but you should always aim for readability.

Blank lines

Single vs double quotes

PEP-8 does not specify which to prefer, instead recommending to “[p]ick a rule and stick to it.” In general, you should use single quotes around strings (as the Python interpreter will do), except where using double quotes would avoid escaping a lot of single quotes, thereby “improving readability.” Per PEP-8, “[f]or triple-quoted strings, always use double quote characters to be consistent with the docstring convention in PEP-257.”

Ex:

my_dict['my_key'] = 'my_value'

sample = 'This is a string.'

double_sample = "I've double quoted this string, can't you see that it's better?"

"""
This is a doc string.
"""

Dealing with long if clauses

Break up long if clauses by assigning logical parts to variables. Then test the logic of the individual variables.

NO:

if (waist > shoulder * 0.75 and waist <= shoulder * 1.05) or (waist > hips * 0.75 and waist <= hips * 1.05):
    do_something()

YES:

this = waist > shoulder * 0.75 and waist <= shoulder * 1.05
that = waist > hips * 0.75 and waist <= hips * 1.05

if this or that:
    do_something()

Avoid for/else and while/else

Python allows the construction of for/else and while/else loops where the else clause is executed if the loop ends normally without encountering a break.

Ex:

for thing in container:
    if something_about(thing):
        # Found it!
        do_something(thing)
        break
else:
    # Didn't find it..
    no_such_thing()

While offering some syntactic advantages, the syntax is confusing and even core Python developers believe it probably should not be used.

Do not use else clauses on your for or while loops. It will likely result in subtle bugs that are difficult to debug. It also makes code review harder, since the reviewing may have to try and guess at the intention of the code if the use of else may be ambiguous (e.g., near a for loop nested in an if conditional).

Class inheritance and using mixins

"Mixins" are typically small classes that encapsulate oft-used functionality to add to other base classes, used to keep code "DRY". These are particularly liberally used along with class-based views in web frameworks like Django and Django REST Framework. Python allows a class to inherit from multiple classes, but the order of inheritance might seem counterintuitive if you aren't familiar with Python internals. Just try and commit these two basic rules to memory:

For example:

from django.db import models


class PrettyPrintModelMixin(object):

    def pretty_print(self):
        print '** {0}: {1} **'.format(self.__class__.__name__, self.name)


class MyFancyModel(PrettyPrintModelMixin, models.Model):

    name = models.CharField()
    timestamp = models.DateTimeField(auto_now_add=True)

If you are interacting with this model in the Python interpreter, you might see:

>>> fancy = MyFancyModel(name='Beetlejuice')
>>> fancy.pretty_print()
** MyFancyModel: Beetlejuice **

Imports

As noted in PEP-8, imports should generally come one on a line. When importing just a few classes or functions from a module, you can list them on a single line like so:

from django.db.models import Q, Max, Count

Sometimes verbose class names make it difficult to keep things on a single line. You can use the following line continuation:

from shopper.serializers import (
    ShopperSerializer, RegisterFacebookSerializer, RegisterTwitterSerializer,
    RegisterTiooSerializer, ShopperWithTokenSerializer, RequestPasswordResetSerializer)

If you're importing more than a few classes/methods and require more than three lines total, either import the entire module or consider a refactor to make the code more succinct and clear. Typing out the full namespace can become cumbersome or verbose; in such a case, use import ... as, e.g.:

import yezzimixins as mxn

This allows you to do something like the following:

class MyView(mxn.FormatMixin, mxn.LocaleMixin):

It's also helpful to group related imports into import "blocks". You should group Django-specific, Django REST Framework-specific, and app-specific imports together, followed by general Python module imports—in that order. A single blank line should separate each block as shown below:

from django.core.urlresolvers import reverse

from rest_framework import status
from rest_framework.test import APITestCase

from models import Shopper
from tokens.models import FacebookToken, PasswordResetToken

from mock import patch
import requests