Naming

Most of the standard library was built keeping usability in mind. Python, in this case, can be compared to the pseudocode you might think about when working on a program. Most of the code can be read out loud. For instance, this snippet could be understood even by someone that is not a programmer:

my_list = []
if 'd' not in my_list:
    my_list.append('d')

The fact that Python code is so close to natural language is one of the reasons why Python is so easy to learn and use. When you are writing a program, the flow of your thoughts is quickly translated into lines of code.

1. PEP 8

PEP 8 (http://www.python.org/dev/peps/pep-0008) provides a style guide for writing Python code. Besides some basic rules, such as indentation, maximum line length, and other details concerning the code layout, PEP 8 also provides a section on naming conventions that most of the code bases follow.

This section provides only a quick summary of PEP 8, and a handy naming guide for each kind of Python syntax element. You should still consider reading the PEP 8 document as mandatory.

1.1. Why and when to follow PEP 8?

If you are creating a new software package that is intended to be open sourced, you should always follow PEP 8 because it is a widely accepted standard and is used in most of the open source projects written in Python. If you want to foster any collaboration with other programmers, then you should definitely stick to PEP 8, even if you have different views on the best code style guidelines. Doing so has the benefit of making it a lot easier for other developers to jump straight into your project. Code will be easier to read for newcomers because it will be consistent in style with most of the other Python open source packages.

Also, starting with full PEP 8 compliance saves you time and trouble in the future. If you want to release your code to the public, you will eventually face suggestions from fellow programmers to switch to PEP 8. Arguments as to whether it is really necessary to do so for a particular project tend to be never-ending flame wars that are impossible to win. This is the sad truth, but you may be eventually forced to be consistent with it or risk losing valuable contributors.

Also, restyling of the whole project’s code base if it is in a mature state of development might require a tremendous amount of work. In some cases, such restyling might require changing almost every line of code. While most of the changes can be automated (indentation, newlines, and trailing whitespaces), such massive code overhaul usually introduces a lot of conflicts in every version control workflow that is based on branching. It is also very hard to review so many changes at once. These are the reasons why many open source projects have a rule that style-fixing changes should always be included in separate pull/merge requests or patches that do not affect any feature or bug.

1.2. Team-specific style guidelines

Despite providing a comprehensive set of style guidelines, PEP 8 still leaves some freedom for the developers. Especially in terms of nested data literals and multiline function calls that require long lists of arguments. Some teams may decide that they require additional styling rules and the best option is to formalize them in some kind of document that is available for every team member.

Also, in some situations, it may be impossible or economically infeasible to be strictly consistent with PEP 8 in some old projects that had no style guide defined. Such projects will still benefit from formalization of the actual coding conventions even if they do not reflect the official set of PEP 8 rules. Remember, what is more important than consistency with PEP 8 is consistency within the project. If rules are formalized and available as a reference for every programmer, then it is way easier to keep consistency within a project and organization.

2. Naming styles

The different naming styles used in Python are:

  • CamelCase

  • mixedCase

  • UPPERCASE and UPPER_CASE_WITH_UNDERSCORES

  • lowercase and lower_case_with_underscores

  • _leading and trailing_ underscores, and sometimes __doubled__ underscores

Lowercase and uppercase elements are often a single word, and sometimes a few words concatenated. With underscores, they are usually abbreviated phrases. Using a single word is better. The leading and trailing underscores are used to mark the privacy and special elements.

These styles are applied to the following:

  • Variables

  • Functions and methods

  • Properties

  • Classes

  • Modules

  • Packages

2.1. Variables

There are the following two kinds of variables in Python:

  • Constants: These define values that are not supposed to change during program execution

  • Public and private variables: These hold the state of applications that can change during program execution

2.1.1. Constants

For constant global variables, an uppercase with an underscore is used. It informs the developer that the given variable represents a constant value.

Note

There are no real constants in Python like those in C++, where const can be used. You can change the value of any variable. That’s why Python uses a naming convention to mark a variable as a constant.

For example, the doctest module provides a list of option flags and directives (http://docs.python.org/lib/doctest-options.html) that are small sentences, clearly defining what each option is intended for, for example:

from doctest import IGNORE_EXCEPTION_DETAIL
from doctest import REPORT_ONLY_FIRST_FAILURE

These variable names seem rather long, but it is important to clearly describe them. Their usage is mostly located in the initialization code rather than in the body of the code itself, so this verbosity is not annoying.

Note

Abbreviated names obfuscate the code most of the time. Don’t be afraid of using complete words when an abbreviation seems unclear.

Some constants’ names are also driven by the underlying technology. For instance, the os module uses some constants that are defined on the C side, such as the EX_XXX series, that defines UNIX exit code numbers. Same name code can be found, as in the following example, in the system’s sysexits.h C headers files:

import os
import sys


sys.exit(os.EX_SOFTWARE)

Another good practice when using constants is to gather all of them at the top of a module that uses them. It is also common to combine them under new variables if they are flags or enumerations that allow for such operations, for example:

import doctest
TEST_OPTIONS = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_ONLY_FIRST_FAILURE)
2.1.1.1. Naming and usage

Constants are used to define a set of values the program relies on, such as the default configuration filename.

A good practice is to gather all the constants in a single file in the package. That is how Django works, for instance. A module named settings.py provides all the constants as follows:

SQL_USER = 'tarek'
SQL_PASSWORD = 'secret'
SQL_URI = 'postgres://%s:%s@localhost/db' % (SQL_USER, SQL_PASSWORD)
MAX_THREADS = 4

Another approach is to use a configuration file that can be parsed with the ConfigParser module, or another configuration parsing tool. But some people argue that it is rather an overkill to use another file format in a language such as Python, where a source file can be edited and changed as easily as a text file.

For options that act like flags, a common practice is to combine them with Boolean operations, as the doctest and re modules do. The pattern taken from doctest is quite simple, as shown in the following code:

OPTIONS = {}

def register_option(name):
    return OPTIONS.setdefault(name, 1 << len(OPTIONS))

def has_option(options, name):
    return bool(options & name)

# now defining options
BLUE = register_option('BLUE')
RED = register_option('RED')
WHITE = register_option('WHITE')

This code allows for the following usage:

>>> # let's try them
>>> SET = BLUE | RED
>>> has_option(SET, BLUE)
True
>>> has_option(SET, WHITE)
False

When you define a new set of constants, avoid using a common prefix for them, unless the module has several independent sets of options. The module name itself is a common prefix.

Another good solution for option-like constants would be to use the Enum class from the built-in enum module and simply rely on the set collection instead of the binary operators.

Important

Using binary bit-wise operations to combine options is common in Python. The inclusive OR ( | ) operator will let you combine several options in a single integer, and the AND ( & ) operator will let you check that the option is present in the integer (refer to the has_option function).

2.1.2. Public and private variables

For global variables that are mutable and freely available through imports, a lowercase letter with an underscore should be used when they do not need to be protected. If a variable shouldn’t be used and modified outside of its origin module we consider it a private member of that module. A leading underscore, in that case, can mark the variable as a private element of the package, as shown in the following code:

_observers = []

def add_observer(observer):
    _observers.append(observer)

def get_observers():
    """Makes sure _observers cannot be modified."""
    return tuple(_observers)

Variables that are located in functions, and methods, follow the same rules as public variables and are never marked as private since they are local to the function context.

For class or instance variables, you should use the private marker (the leading underscore) if making the variable a part of the public signature does not bring any useful information, or is redundant. In other words, if the variable is used only internally for the purpose of some other method that provides an actual public feature, it is better to make it private.

For instance, the attributes that are powering a property are good private citizens, as shown in the following code:

class Citizen(object):

    def __init__(self, first_name, last_name):
        self._first_name = first_name
        self._last_name = last_name

    @property
    def full_name(self):
        return f"{self._first_name} {self._last_name}"

Another example would be a variable that keeps some internal state that should not be disclosed to other classes. This value is not useful for the rest of the code, but participates in the behavior of the class:

class UnforgivingElephant(object):

    def __init__(self, name):
        self.name = name
        self._people_to_stomp_on = []

    def get_slapped_by(self, name):
        self._people_to_stomp_on.append(name)
        print('Ouch!')

    def revenge(self):
        print('10 years later...')
        for person in self._people_to_stomp_on:
            print('%s stomps on %s' % (self.name, person))

Here is what you’ll see in an interactive session:

>>> joe = UnforgivingElephant('Joe')
>>> joe.get_slapped_by('Tarek')
Ouch!
>>> joe.get_slapped_by('Bill')
Ouch!
>>> joe.revenge()
10 years later...
Joe stomps on Tarek
Joe stomps on Bill

2.2. Functions and methods

Functions and methods should be in lowercase with underscores. This rule was not always true in the old standard library modules. Python 3 did a lot of reorganization of the standard library, so most of the functions and methods have a consistent letter case. Still, for some modules such as threading, you can access the old function names that used mixedCase (for example, currentThread). This was left to allow easier backward compatibility, but if you don’t need to run your code in older versions of Python, then you should avoid using these old names.

This way of writing methods was common before the lowercase norm became the standard, and some frameworks, such as Zope and Twisted, are also still using mixedCase for methods. The community of developers working with them is still quite large. So the choice between mixedCase and lowercase with an underscore is definitely driven by the libraries you are using.

As a Zope developer, it is not easy to stay consistent because building an application that mixes pure Python modules and modules that import Zope code is difficult. In Zope, some classes mix both conventions because the code base is still evolving and Zope developers try to adopt the common conventions accepted by so many.

A decent practice in this kind of library environment is to use mixedCase only for elements that are exposed in the framework, and to keep the rest of the code in PEP 8 style.

It is also worth noting that developers of the Twisted project took a completely different approach to this problem. The Twisted project, same as Zope, predates the PEP 8 document. It was started when there were no official guidelines for Python code style, so it had its own guidelines. Stylistic rules about the indentation, docstrings, line lengths, and so on could be easily adopted. On the other hand, updating all the code to match naming conventions from PEP 8 would result in completely broken backward compatibility. And doing that for such a large project as Twisted is infeasible. So Twisted adopted as much of PEP 8 as possible and left things such as mixedCase for variables, functions, and methods as part of its own coding standard. And this is completely compatible with the PEP 8 suggestion because it exactly says that consistency within a project is more important than consistency with PEP 8’s style guide.

2.2.1 The private controversy

For private methods and functions, we usually use a single leading underscore. This is only a naming convention and has no syntactical meaning. But it doesn’t mean that leading underscores have no syntactical meaning at all. When a method has two leading underscores, it is renamed on the fly by the interpreter to prevent a name collision with a method from any subclass. This feature of Python is called name mangling.

So some people tend to use a double leading underscore for their private attributes to avoid name collision in the subclasses, for example:

class Base(object):
    def __secret(self):
        print("don't tell")
    def public(self):
        self.__secret()

class Derived(Base):
    def __secret(self):
        print("never ever")

From this you will see the following output:

>>> Base.__secret
Traceback (most recent call last):
    File "<input>", line 1, in <module>
AttributeError: type object 'Base' has no attribute '__secret'
>>> dir(Base)
['_Base__secret', ..., 'public']
>>> Base().public()
don't tell
>>> Derived().public()
don't tell

The original motivation for name mangling in Python was not to provide the same isolation primitive as a private keyword in C++ but to make sure that some base classes implicitly avoid collisions in subclasses, especially if they are intended to be used in multiple inheritance contexts (for example, as mixin classes). But using it for every attribute that isn’t public obfuscates the code and makes it extremely hard to extend. This is not Pythonic at all.

For more information on this topic, an interesting thread occurred in the Python-Dev mailing list many years ago, where people argued on the utility of name mangling and its fate in the language. It can be found at

http://mail.python.org/pipermail/python-dev/2005-December/058555.html .

2.2.2. Special methods

Special methods (https://docs.python.org/3/reference/datamodel.html#special-method-names) start and end with a double underscore and form so-called protocols of the language. Some developers used to call them dunder methods as a portmanteau of double underscore. They are used for operator overloading, container definitions, and so on. For the sake of readability, they should be gathered at the beginning of class definitions, as shown in the following code:

class WeirdInt(int):

    def __add__(self, other):
        return int.__add__(self, other) + 1

    def __repr__(self):
        return '<weirdo %d>' % self

    # public API
    def do_this(self):
        print('this')

    def do_that(self):
        print('that')

No user-defined method should use this convention unless it explicitly has to implement one of the Python object protocols. So don’t invent your own dunder methods such as this:

class BadHabits:
    def __my_method__(self):
        print('ok')

2.3. Arguments

Arguments are in lowercase, with underscores if needed. They follow the same naming rules as variables because arguments are simply local variables that get their value as function input values. In the following example, text and separator are arguments of one_line() function:

def one_line(text, separator=" "):
"""Convert possibly multiline text to single line"""
    return separator.join(text.split())

2.4. Properties

The names of properties are in lowercase, or in lowercase with underscores. Most of the time they represent an object’s state, which can be a noun or an adjective, or a small phrase when needed. In the following code example, the Container class is a simple data structure that can return copies of its contents through unique_items and ordered_items properties:

class Container:
    _contents = []
    def append(self, item):
        self._contents.append(item)

    @property
    def unique_items(self):
        return set(self._contents)
    @property
    def ordered_items(self):
        return list(self._contents)

2.5. Classes

The names of classes are always in CamelCase, and may have a leading underscore when they are private to a module.

In object-oriented programming classes are used to encapsulate the application state. Attributes of objects are record of that state. Methods are used to modify that state, convert it into meaningful values or to produce side effects. This is why class names are often noun phrases and form a usage logic with the method names that are verb phrases. The following code example contains a Document class definition with a single save() method:

class Document():
    file_name: str
    contents: str
    ...

    def save(self):
        with open(self.file_name, 'w') as file:
            file.write(self.contents)

Class instances often use the same noun phrases as the document but spelled with lowercase. So, actual Document class usage could be as follows:

new_document = Document()
new_document.save()

2.6. Modules and packages

Besides the special module __init__, the module names are in lowercase. The following are some examples from the standard library:

  • os

  • sys

  • shutil

The Python standard library does not use underscores for module names to separate words but they are used commonly in many other projects. When the module is private to the package, a leading underscore is added. Compiled C or C++ modules are usually named with an underscore and imported in pure Python modules. Package names follow the same rules, since they act more like structured modules.

3. Naming guide

A common set of naming rules can be applied on variables, methods, functions, and properties. The names of classes and modules play a very important role in namespace construction and greatly affect code readability. This section contains a miniguide that will help you to define meaningful and readable names for your code elements.

3.1. Using the has/is prefixes for Boolean elements

When an element holds a Boolean value you can mark it with is and/or has syntax to make the variable more readable. In the following example, is_connected and has_cache are such identifiers that hold Boolean states of the DB class instances:

class DB:
is_connected = False
has_cache = False

3.2. Using plurals for variables that are collections

When an element is holding a sequence, it is a good idea to use a plural form. You can also do the same for various mapping variables and properties. In following example, connected_users and tables are class attributes that hold multiple values:

class DB:
    connected_users = ['Tarek']
    tables = {'Customer':['id', 'first_name', 'last_name']}

3.3. Using explicit names for dictionaries

When a variable holds a mapping, you should use an explicit name when possible. For example, if a dict holds a person’s address, it can be named persons_addresses:

persons_addresses = {'Bill': '6565 Monty Road', 'Pamela': '45 Python street'}

3.4. Avoid generic names and redundancy

You should generally avoid using explicit type names list, dict, and set as parts of variable names even for local variables. Python now offers function and variable annotations and a typing hierarchy that allows you to easily mark an expected type for a given variable so there is no longer a need to describe object types in their names. It makes the code hard to read, understand, and use. Using a built-in name has to be avoided as well to avoid shadowing it in the current namespace. Generic verbs should also be avoided, unless they have a meaning in the namespace.

Instead, domain-specific terms should be used as follows:

def compute(data): # too generic
    for element in data:
        yield element ** 2

def squares(numbers): # better
    for number in numbers:
        yield number ** 2

There is also the following list of prefixes and suffixes that, despite being very common in programming, should be, in fact, avoided in function and class names:

  • Manager

  • Object

  • Do, handle, or perform

The reason for this is that they are vague, ambiguous, and do not add any value to the actual name. Jeff Atwood, the co-founder of Discourse and Stack Overflow, has a very good article on this topic and it can be found on his blog at http://blog.codinghorror.com/i-shall-call-it-somethingmanager/

There is also a list of package names that should be avoided. Everything that does not give any clue about its content can do a lot of harm to the project in the long term. Names such as misc, tools, utils, common, or core have a very strong tendency to become endless bags of various unrelated code pieces of very poor quality that seem to grow in size exponentially. In most cases, the existence of such a module is a sign of laziness or lack of enough design efforts. Enthusiasts of such module names can simply forestall the future and rename them to trash or dumpster because this is exactly how their teammates will eventually treat such modules.

In most cases, it is almost always better to have more small modules even with very little content but with names that reflect well what is inside. To be honest, there is nothing inherently wrong with names such as utils and common and there is a possibility to use them responsibly. But reality shows that in many cases they instead become a stub for dangerous structural antipatterns that proliferate very fast. And if you don’t act fast enough, you may not be able get rid of them ever. So the best approach is simply to avoid such risky organizational patterns and nip them in the bud.

3.5. Avoiding existing names

It is a bad practice to use names that shadow other names that already exist in the same context. It makes code reading and debugging very confusing. Always try to define original names, even if they are local to the context. If you eventually have to reuse existing names or keywords, use a trailing underscore to avoid name collision, for example:

def xapian_query(terms, or_=True):
    """if or_ is true, terms are combined with the OR clause"""
    ...

Note that the class keyword is often replaced by klass or cls:

def factory(klass, *args, **kwargs):
    return klass(*args, **kwargs)

4. Best practices for arguments

The signatures of functions and methods are the guardians of code integrity. They drive its usage and build its APIs. Besides the naming rules that we have discussed previously, special care has to be taken for arguments. This can be done through the following three simple rules:

  • Build arguments by iterative design.

  • Trust the arguments and your tests.

  • Use *args and **kwargs magic arguments carefully.

4.1. Building arguments by iterative design

Having a fixed and well-defined list of arguments for each function makes the code more robust. But this can’t be done in the first version, so arguments have to be built by iterative design. They should reflect the precise use cases the element was created for, and evolve accordingly.

Consider the following example of the first versions of some Service class:

class Service: # version 1

    def _query(self, query, type):
        print('done')

    def execute(self, query):
        self._query(query, 'EXECUTE')

If you want to extend the signature of the execute() method with new arguments in a way that preserves backward compatibility, you should provide default values for these arguments as follows:

class Service(object): # version 2

    def _query(self, query, type, logger):
        logger('done')

    def execute(self, query, logger=logging.info):
        self._query(query, 'EXECUTE', logger)

The following example from an interactive session presents two styles of calling the execute() method of the updated Service class:

>>> Service().execute('my query')
# old-style call
>>> Service().execute('my query', logging.warning)
WARNING:root:done

4.2. Trusting the arguments and your tests

Given the dynamic typing nature of Python, some developers use assertions at the top of their functions and methods to make sure the arguments have proper content, for example:

def divide(dividend, divisor):
    assert isinstance(dividend, (int, float))
    assert isinstance(divisor, (int, float))
    return dividend / divisor

This is often done by developers who are used to static typing and feel that something is missing in Python.

This way of checking arguments is a part of the Design by Contract (DbC) programming style, where preconditions are checked before the code is actually run.

The two main problems in this approach are as follows:

  • DbC’s code explains how it should be used, making it less readable

  • This can make it slower, since the assertions are made on each call

The latter can be avoided with the -O option of the Python interpreter. In that case, all assertions are removed from the code before the byte code is created, so that the checking is lost.

In any case, assertions have to be done carefully, and should not be used to bend Python to a statically typed language. The only use case for this is to protect the code from being called nonsensically. If you really want to have some kind of static typing in Python, you should definitely try MyPy or a similar static type checker that does not affect your code runtime and allows you to provide type definitions in a more readable form as function and variable annotations.

4.3. Using *args and **kwargs magic arguments carefully

The *args and **kwargs arguments can break the robustness of a function or method. They make the signature fuzzy, and the code often starts to become a small argument parser where it should not, for example:

def fuzzy_thing(**kwargs):
    if 'do_this' in kwargs:
        print('ok i did this')

    if 'do_that' in kwargs:
        print('that is done')

    print('ok')

>>> fuzzy_thing(do_this=1)
ok i did this
ok
>>> fuzzy_thing(do_that=1)
that is done
ok
>>> fuzzy_thing(what_about_that=1)
ok

If the argument list gets long and complex, it is tempting to add magic arguments. But this is more a sign of a weak function or method that should be broken into pieces or refactored.

When *args is used to deal with a sequence of elements that are treated the same way in the function, asking for a unique container argument such as an iterator is better, for example:

def sum(*args): # okay
    total = 0
    for arg in args:
        total += arg

    return total


def sum(sequence): # better!
    total = 0
    for arg in sequence:
        total += arg

    return total

For **kwargs, the same rule applies. It is better to fix the named arguments to make the method’s signature meaningful, for example:

def make_sentence(**kwargs):
    noun = kwargs.get('noun', 'Bill')
    verb = kwargs.get('verb', 'is')
    adjective = kwargs.get('adjective', 'happy')
    return f'{noun} {verb} {adjective}'

def make_sentence(noun='Bill', verb='is', adjective='happy'):
    return f'{noun} {verb} {adjective}'

Another interesting approach is to create a container class that groups several related arguments to provide an execution context. This structure differs from *args or **kwargs because it can provide internals that work over the values, and can evolve independently. The code that uses it as an argument will not have to deal with its internals.

For instance, a web request passed on to a function is often represented by an instance of a class. This class is in charge of holding the data passed by the web server, as shown in the following code:

def log_request(request): # version 1
    print(request.get('HTTP_REFERER', 'No referer'))

def log_request(request): # version 2
    print(request.get('HTTP_REFERER', 'No referer'))
    print(request.get('HTTP_HOST', 'No host'))

Magic arguments cannot be avoided sometimes, especially in metaprogramming. For instance, they are indispensable in the creation of decorators that work on functions with any kind of signature.

5. Class names

The name of a class has to be concise, precise, and descriptive. A common practice is to use a suffix that informs about its type or nature, for example:

  • SQLEngine

  • MimeTypes

  • StringWidget

  • TestCase

For base or abstract classes, a Base or Abstract prefix can be used as follows:

  • BaseCookie

  • AbstractFormatter

The most important thing is to be consistent with the class attributes. For example, try to avoid redundancy between the class and its attributes’ names as follows:

>>> SMTP.smtp_send()  # redundant information in the namespace
>>> SMTP.send()  # more readable and mnemonic

6. Modules and packages

The module and package names inform about the purpose of their content. The names are short, in lowercase, and usually without underscores, for example:

  • sqlite

  • postgres

  • sha1

They are often suffixed with lib if they are implementing a protocol, as in the following:

import smtplib
import urllib
import telnetlib

When choosing a name for a module, always consider its content and limit the amount of redundancy within the whole namespace, for example:

from widgets.stringwidgets import TextWidget # bad
from widgets.strings import TextWidget # better

When a module is getting complex and contains a lot of classes, it is a good practice to create a package and split the module’s elements into other modules.

The __init__ module can also be used to put back some common APIs at the top level of the package. This approach allows you to organize the code into smaller components without reducing the ease of use.

7. Useful tools

Common conventions and practices used in a software project should always be documented. But having proper documentation for guidelines is often not enough to enforce that these guidelines are actually followed. Fortunately, you can use automated tools that can check sources of your code and verify if it meets specific naming conventions and style guidelines.

The following are a few popular tools:

  • pylint: This is a very flexible source code analyzer

  • pycodestyle and flake8: This is a small code style checker and a wrapper that adds to it some more useful features, such as static analysis and complexity measurement

7.1. Pylint

Besides some quality assurance metrics, Pylint allows for checking of whether a given source code is following a naming convention. Its default settings correspond to PEP 8 and a Pylint script provides a shell report output.

To install Pylint, you can use pip as follows:

$ pip install pylint

After this step, the command is available and can be run against a module, or several modules using wildcards. Let’s try it on Buildout’s bootstrap.py script as follows:

$ wget -O bootstrap.py https://bootstrap.pypa.io/bootstrap-buildout.py -q
$ pylint bootstrap.py
No config file found, using default configuration
************* Module bootstrap
C: 76, 0: Unnecessary parens after 'print' keyword (superfluous-parens)
C: 31, 0: Invalid constant name "tmpeggs" (invalid-name)
C: 33, 0: Invalid constant name "usage" (invalid-name)
C: 45, 0: Invalid constant name "parser" (invalid-name)
C: 74, 0: Invalid constant name "options" (invalid-name)
C: 74, 9: Invalid constant name "args" (invalid-name)
C: 84, 4: Import "from urllib.request import urlopen" should be placed at
the top of the module (wrong-import-position)
...
Global evaluation
-----------------
Your code has been rated at 6.12/10

Real Pylint’s output is a bit longer and here it has been truncated for the sake of brevity.

Remember that Pylint can often give you false positive warnings that decrease the overall quality rating. For instance, an import statement that is not used by the code of the module itself is perfectly fine in some cases (for example, building top-level __init__ modules in a package). Always treat Pylint’s output as a hint and not an oracle.

Making calls to libraries that are using mixedCase for methods can also lower your rating. In any case, the global evaluation of your code score is not that important. Pylint is just a tool that points you to places where there is the possibility for improvements.

It is always recommended to do some tuning of Pylint. In order to do so you need to create a .pylinrc configuration file in your project’s root directory. You can do that using the following -generate-rcfile option of the pylint command:

$ pylint --generate-rcfile > .pylintrc

This configuration file is self-documenting (every possible option is described with comment) and should already contain every available Pylint configuration option.

Besides checking for compliance with some arbitrary coding standards, Pylint can also give additional information about the overall code quality, such as:

  • Code duplication metrics

  • Unused variables and imports

  • Missing function, method, or class docstrings

  • Too long function signatures

The list of available checks that are enabled by default is very long. It is important to know that some of the rules are very arbitrary and cannot always be easily applied to every code base. Remember that consistency is always more valuable than compliance to some arbitrary rules. Fortunately, Pylint is very tunable, so if your team uses some naming and coding conventions that are different from the ones assumed by default, you can easily configure Pylint to check for consistency with your own conventions.

7.2. pycodestyle and flake8

pycodestyle (formerly pep8) is a tool that has only one purpose; it provides only style checking against code conventions defined in PEP 8. This is the main difference from Pylint that has many more additional features. This is the best option for programmers that are interested in automated code style checking only for the PEP 8 standard, without any additional tool configuration, as in Pylint’s case.

pycodestyle can be installed with pip as follows:

$ pip install pycodestyle

When run on the Buildout’s bootstrap.py script, it will give the following short list of code style violations:

$ wget -O bootstrap.py https://bootstrap.pypa.io/bootstrap-buildout.py -q
$ pycodestyle bootstrap.py
bootstrap.py:118:1: E402 module level import not at top of file
bootstrap.py:119:1: E402 module level import not at top of file
bootstrap.py:190:1: E402 module level import not at top of file
bootstrap.py:200:1: E402 module level import not at top of file

The main difference from Pylint’s output is its length. pycodestyle concentrates only on style, so it does not provide any other warnings, such as unused variables, too long function names, or missing docstrings. It also does not give a rating. And it really makes sense because there is no such thing as partial consistency or partial conformance. Any, even the slightest, violation of style guidelines makes the code immediately inconsistent.

The code of pycodestyle is simpler than Pylint’s and its output is easier to parse, so it may be a better choice if you want to make your code style verification part of a continuous integration process. If you are missing some static analysis features, there is the flake8 package that is a wrapper on pycodestyle and a few other tools that are easily extendable and provide a more extensive suite of features. These include the following:

  • McCabe complexity measurement

  • Static analysis via pyflakes

  • Disabling whole files or single lines using comments