Python

The general rule when writing Python code is to follow PEP 8. The rules given later in this document override what is said in PEP 8.

Be tolerant of code that doesn’t follow these conventions: our code base has been evolving over years and doesn’t always match the current style as we update these rules. Also, we want to reuse software written for other projects that do not adhere to our rules.

Remember that PEP 8 explicitly allows breaking a rule in the interest of keeping code consistent.

A reasonable goal is that code covered by the ZPL should follow these conventions.

License statement, module docstring

Python files should always contain the most current license comment at the top followed by the module documentation string.

Here is the template:

##############################################################################
#
# Copyright (c) 2016 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""One-line summary goes here.

Module documentation goes here.
"""

Whitespace

Trailing whitespace should not occur, nor should multiple blank lines at the end of files.

Import statements

All imports should be at the top of the module, after the module docstring and/or comments, but before module globals.

It is sometimes necessary to violate this to address circular import problems. If this is the case, add a comment to the import section at the top of the file to flag that this was done.

Refrain from using relative imports. Instead of:

import .foo  # from same package

you can write:

from zope.package.module import foo

Attribute and method names

The naming of attributes as recommended by PEP 8 is controversial. PEP 8 prefers attribute_name over attributeName. Newer code tends to prefer the use of underscores over camel case. However, Zope has been built originally with the latter rule and a lot of code still use this scheme.

Boolean-type attributes should always form a true-false-question, typically using “has” or “is” as prefix. Example: is_required instead of required.

Method names should always start with a verb that describes the action.

Examples:

good:
first_name
is_required
execute_command()
save()
convert_value_to_string()

bad:
FirstName
required
command()
string()

Local variables

Single-letter variable names should be avoided unless:

  • Their meaning is extremely obvious from the context, and
  • Brevity is desirable

The most obvious case for single-letter variables is for iteration variables.

try/except blocks

try blocks should cover as little code as possible. except statements should match exceptions as specific as possible.

For example, if you are converting a value to an int, and you want to catch conversion errors, you need only catch ValueError. Be sure to do the minimum possible between your try: and except ValueError: statements:

try:
    int(x)
except ValueError:
    ...

String handling

Use startswith and endswith because it is faster, cleaner and less error-prone than comparing sliced strings:

# Yes:
if foo.startswith('bar'):
    ...
if foo.endswith('.html'):
    ...

# No:
if foo[:3]=='bar':
    ...
if foo[-5:]=='.html':
    ...

Type checks

Constructs like if type(obj) is type('') should be replaced using isinstance():

# Yes:
if isinstance(obj, int):
  ...

# No:
if type(obj) is type(1):
  ...
if type(obj) is int:

Marker objects

Use instances of object if you need to construct marker objects (for example when detecting default values). Compare them using is as recommended by PEP 8.

Interfaces

Interface names adhere to PEP 8’s naming of classes, except that they are prefixed with a capital I, as in IMagicThing.

One function of interfaces is to document functionality, so be very verbose with the documentation strings.

All public interfaces should go into a file called interfaces.py. “Public” interfaces are those that you expect to be implemented more than once. Interfaces that are likely to be implemented only once, like IGlobalAdapterService, should live in the same module as their implementation.