Skip to content

Latest commit

 

History

History
368 lines (300 loc) · 10.3 KB

README.md

File metadata and controls

368 lines (300 loc) · 10.3 KB

Python Style Guide

Table of Contents

  1. Inports
  2. Indentation
  3. Techniques

Inports

  • 1.1 Split imports into 3 categories (stdlib, 3rdparty, internal imports), relative imports should be the last. Try to keep them sorted.

    import datetime
    import logging
    
    from django.utils import timezone
    from django.utils.translation import ugettext_lazy as _
    from django.conf import settings
    from django_states.machine import StateMachine, StateDefinition, StateGroup, StateTransition
    from django_states.exceptions import TransitionValidationError
    
    from ourapp.notifications.handlers import NOTIFICATION_METHOD
    from ourapp.notifications import notifiers
    from ourapp.module_a.provider import Trusted
    from ourapp.activation.models import XOrder
    from ourapp.porting.utils import get_closest_wish_date
    from .models import Inport
    from .utils import add_inport_s3

  • 1.2 Use imports for packages and modules only, if it is possible.

    import datetime  # never: from datetime import datetime or similar
    import logging  # never: from logging import getLogger
    import mock  # never from mock import Mock
    import random
    import uuid
    
    import bson  # never from bson import ObjectId
    from django.utils import timezone  # never: from django.utils.timezone import now

  • 1.3 If import exceeded line length limit, format it like that:

    from ourapp.porting.tasks import (
        task_a,
        task_b,
        task_c,  # <- comma here
    )

Indentation

  • 2.1 function calls

    abc = qwe(dsadsa(product.to_dict(), cls=DecimalEncoder, mimetype='application/json'))  # bad
    
    abc = qwe(dsadsa(product.to_dict(), cls=DecimalEncoder,
              mimetype='application/json'))  # bad
    
    
    abc = qwe(dsadsa(product.to_dict(),
              cls=DecimalEncoder,
              mimetype='application/json'))  # bad
    
    
    abc = qwe(dsadsa(
        product.to_dict(),
        cls=DecimalEncoder,
        mimetype='application/json',  # <- comma here
    ))  # OK
    
    abc = qwe(
        dsadsa(
            product.to_dict(),
            cls=DecimalEncoder,
            mimetype='application/json',  # <- comma here
        )
    )  # could be too
    
    # especially when qwe has one aditional keyword parameter:
    
    abc = qwe(
        dsadsa(
            product.to_dict(),
            cls=DecimalEncoder,
            mimetype='application/json',  # <- comma here
        ),  # <- comma here
        dupa=2,  # <- comma here
    )  # OK

  • 2.2 dict class

    # bad
    abc = dict(abc=2, cde=5, mama='tata', tata='mama', raz=1, dwa=2)
    
    # bad
    abc = dict(abc=2, cde=5, mama='tata',
               tata='mama', raz=1, dwa=2)
    
    # bad  
    abc = dict(
        abc=2, cde=5, mama='tata',
        tata='mama', raz=1, dwa=2
    )
    
    # bad
    abc = dict(
        abc=2, cde=5, mama='tata',
        tata='mama', raz=1, dwa=2,
    )
    
    # good
    abc = dict(
        abc=2,
        cde=5,
        mama='tata',
        tata='mama',
        raz=1,
        dwa=2,  # <- comma here !
    )    

  • 2.3 dict curly brackets

    # bad
    abc = {'abc': 2, 'cde': 5, 'mama': 'tata', 'tata': 'mama', 'raz': 1, 'dwa': 2}
    
    # bad
    abc = {'abc': 2, 'cde': 5, 'mama': 'tata',
           'tata': 'mama', 'raz': 1, 'dwa': 2}
    
    # bad    
    abc = {
        'abc': 2, 'cde': 5, 'mama': 'tata',
        'tata': 'mama', 'raz': 1, 'dwa': 2
    
    }
    
    # bad
    abc = {
        'abc': 2, 'cde': 5, 'mama': 'tata',
        'tata': 'mama', 'raz': 1, 'dwa': 2,
    }
    
    # good
    abc = {
        'abc': 2,
        'cde': 5,
        'mama': 'tata',
        'tata': 'mama',
        'raz': 1,
        'dwa': 2,  # <- comma here !
    }

  • 2.4 func call and dicts

      abc = abc({
          'a': 1,
          'b': 3,
          'c': 4,  # <- comma here !
      })
    
      abc = abc(dict(
          a=1,
          b=2,
          c=3,  # <- comma here !
      ))
    
      nokaut_api_params = urllib.urlencode({
          'key': str(nokaut_key),
          'keyword': str(keyword),
          'format': 'xml',
          'method': 'nokaut.Product.getByKeyword',
          'sort_direction': 'price_asc',
          'returnType': 'full',
          'limit': '1',  # <- comma here !
      })

  • 2.5 list comprehension

      # bad
      data = [model_field
             for form_field, model_field in fields if form_field in form]
    
      # bad
      data = [model_field
             for form_field, model_field in fields if form_field in form
      ]
    
      # good
      data = [
          model_field
          for form_field, model_field in fields if form_field in form
      ]
    
      # good (if exceeding line length limit)
      data = [
          model_field
          for form_field, model_field in fields
          if form_field in form
      ]

  • 2.6 dict comprehension

      # bad
      data = {model_field: form[form_field].data
             for form_field, model_field in fields if form_field in form}
    
      # good
      data = {
          model_field: form[form_field].data
          for form_field, model_field in fields if form_field in form
      }
    
      # good (if exceeding line length limit)
      data = {
          model_field: form[form_field].data
          for form_field, model_field in fields
          if form_field in form
      }

  • 2.7 if statement if conditions are too long, store their result in variable with meaningful name

    # bad
    if abc is None and b is True and c == 'mama' \
        and x != 1 or c in ('mama', 'tata') \
        or babcia.age > 100: # checks if thing should be done
        # do something big
    
    # bad
    if (abc is None and b is True and c == 'mama'
        and x != 1 or c in ('mama', 'tata')
        or babcia.age > 100): # checks if thing should be done
        # do something big
    
    # good
    should_be_done = (   # no comment needed !
        abc is None and
        b is True and
        c == 'mama'and
        x != 1 or
        c in ('mama', 'tata') or
        babcia.age > 100
    )
    
    if should_be_done:
        # do something big

  • 2.8 slashes never use them

    # bad:
    def x():
        a_identity = self.aidentityconfirm_set.filter(uuid=self.id_at_provider).first()
        return a_identity.get_person_who_registed_y().get_full_name()\
        if a_identity and a_identity.state == "confirmed" else "A identity is not confirmed yet"
    
    
    # good:
    def x():
        a_identity = self.aidentityconfirm_set.filter(uuid=self.id_at_provider).first()
        if a_identity and a_identity.state == "confirmed":
            return a_identity.get_person_who_registed_y().get_full_name()
        else:
            return "A identity is not confirmed yet"
    
    # there is one exception when you can use slashes, when using chaining methods, examples:
    # django ORM:
    posts = Post.objects.filter(blog__id=x)\
                        .filter(created_at__gt=y)\
                        .filter(desc__contains=z)\
                        .order_by('created_at')
                        
    # Google App Engine NDB
    greetings = Greeting.query_book(ancestor_key)\
                        .filter(Greeting.userid >= 40)\
                        .filter(Greeting.userid < 100)\
                        .fetch(self.GREETINGS_PER_PAGE)
                        
    # SQLAlchemy
    files = File.query.filter(File.used.is_(False))\
                      .filter(File.modified < daysago2)\
                      .yield_per(10)
    
    # mongoalchemy
    place = session.query(Places).filter_by(loc=a)\
                                 .filter(created_at=b)\
                                 .descending('created_at').first()

  • 2.9 Single quotes vs double quotes Use double quotes around strings that are natural language messages and single quotes for small symbol-like strings, for example:

    LIGHT_MESSAGES = {
        'english': "There are 3 lights.",
        'pirate':  "Arr! Thar be 3 lights."
    }
    def lights_message(language, number_of_lights):
        """Return a language-appropriate string reporting the light count."""
        return LIGHT_MESSAGES[language]
    
    def is_pirate(message):
        """Return True if the given message sounds piratical."""
        return re.search(r"(?i)(arr|avast|yohoho)!", message) is not None

    ref: http://stackoverflow.com/a/56190

Techniques

  • 3.1 Bail early is idiomatic coding style.
    # BAD, really bad
    def bad_method(a):
        b = one(a)
        if b.isgood():
            c = two(b)
            if c.isok():
                d = three(c)
                if d.hasX():
                    return d
                else:
                    raise NoXError
             else:
                  raise NotOKError

    # GOOD, more clear, more obvious            
    def good_method(a):
        b = one(a)
        if not b.isgood():
            return
        c = two(b)
        if not c.isok()
            raise NotOKError
        d = three(c)
        if not d.hasX():
            raise NoXError
        return d