The overall structure of a file should be:
- header
- imports
- classes and functions
- unit tests
- main (only for conveyor/__main__.py)
The standard header is:
# vim:ai:et:ff=unix:fileencoding=utf-8:sw=4:ts=4:
# conveyor/path/to/file.extension
#
# conveyor - Printing dispatch engine for 3D objects and their friends.
# Copyright © 2012 Matthew W. Samsonoff <[email protected]>
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, print_function, unicode_literals)
The import order is (from PEP-8):
- standard library imports
- related third party imports
- local application/library specific imports
For brevity, imports of conveyor modules may be written like this:
from conveyor import enum, event
Never use "from module import *". Always list explicitly the names being imported.
Identifier names should follow the Mercurial convention: all lowercase, with no
underbars between words. The exception to this rule is test methods. They
should start with the prefix test_
and should use an underscore whenever a
variant suffix is appended to the test name:
class ExampleTestCase(unittest.TestCase):
def test_method(self):
...
def test_method_ValueError(self):
with self.assertRaises(ValueError):
...
Don't do work in constructors. They must contain only trivial assignment
statements and calls to other constructors. Use a @classmethod
or
@staticmethod
when you need to execute more complicated code during the
construction of an object (a.k.a a named constructor):
class Example(object):
@classmethod
def create(cls, parameter):
value = something(parameter)
example = cls(value)
return example
def __init__(self, value):
self.value = value
It is also acceptable to invoke a 'reset' method from the constructor:
class Example(object):
def __init__(self):
self._reset()
def _reset(self):
self.value = 0
def something(self):
if condition:
self._reset()
The named constructor pattern can be used to enforce initialization requirements:
class Example(object):
@classmethod
def create(cls):
example = cls()
example._initialize()
return example
def __init__(self):
self.value = None
def _initialize(self):
self.value = something()
When a class has only a single named constructor, it should be called create
.
When there is more than one, each named constructor should have a descriptive
name:
class Example(object):
@classmethod
def frompath(cls, path):
...
@classmethod
def fromstream(cls, stream):
...
Avoid both multiple inheritance and super
.
When testing for equality, literals and other constant values should appear on
the left hand of the ==
operator:
if 1 == x:
...
Contrary to PEP 8, the preferred place to break around a binary operator is before the operator, not after it.