blob: ac6205713d5b79258e39d990151b685eb7dfcbbd [file] [log] [blame]
#summary Style guide for Python code contributed to Melange
#labels Contents-Draft,Phase-Guidelines,Importance-Featured,Featured
Melange follows the [http://google-styleguide.googlecode.com/svn/trunk/pyguide.html Google Python Style Guide]; this document describes only those respects in which Melange's style differs from what is laid out in that document.
The [SoCOverview SoC framework], and
[MelangeIntro Melange web applications] built upon it, are implemented in
Python (it is one of the programming language besides Java and Go which are currently supported by
[http://code.google.com/appengine/ Google App Engine]). The Google Python Style Guide and these amendments to it are a
list of dos and don'ts for Python contributions to the Melange project.
The rules below are not guidelines or recommendations, but strict rules. You
may not disregard the rules we list below except as approved on a need-to-use
basis. But note also the advice on consistency at the end of the guide.
If you have questions about these guidelines, you can send mail to the
[MailingListsGuidelines#Developer_list developer mailing list].
Please put *Python style:* in the subject line to make it easier to locate
these discussions in the mailing list archives.
Since this style guide is documented in the Melange wiki, it can be changed via
the existing [DocumentationReviews documentation review process]. However,
changes to the style guide should not be made lightly, since
[PythonStyleGuide#Conclusion consistency] is one of the key goals
of this style guide, and old code will not necessarily be updated for new
style guide changes.
= Summary =
== Python Language Rules ==
# [PythonStyleGuide#Imports Imports]: _Import only modules and only by name *(no wildcard imports)*_
# [PythonStyleGuide#Function_Arguments Function Arguments]: _Pass positional arguments positionally and keyword arguments by keyword._
# [PythonStyleGuide#Threading Threading]: _*For now, do not use* (but it is on its way as part of [https://code.google.com/p/soc/issues/detail?id=1596 issue 1596])_
== Python Style Rules ==
Programs are much easier to maintain when all files have a consistent
style. Here is the canonical Melange Python style.
# [PythonStyleGuide#Semicolons Semicolons]: _*Avoid*_
# [PythonStyleGuide#Line_length Line length]: _*80* columns maximum_
# [PythonStyleGuide#Parentheses Parentheses]: _*Use sparingly*_
# [PythonStyleGuide#Indentation Indentation]: _*2* spaces (*no tabs*),_ *differs from [http://www.python.org/dev/peps/pep-0008/ PEP8]*
# [PythonStyleGuide#Blank_lines Blank lines]: _*2* for functions and classes, *1* for methods_
# [PythonStyleGuide#Whitespace Whitespace]: _Sparingly within a line_
# [PythonStyleGuide#Python_Interpreter Python Interpreter]: _Variable, either omitted, `#!/usr/bin/python`, or `#!/usr/bin/python2.7`_
# [PythonStyleGuide#Comments Comments]: _`__doc__` strings, block, inline_
* [PythonStyleGuide#Doc_strings `__doc__` Strings]
* [PythonStyleGuide#Modules Modules]
* [PythonStyleGuide#Functions_and_Methods Functions and Methods]
* [PythonStyleGuide#Classes Classes]
* [PythonStyleGuide#Block_and_Inline_Comments Block and Inline Comments]
# [PythonStyleGuide#Classes Classes]: _Inherit from object_
# [PythonStyleGuide#Strings Strings]: _Avoid repeated use of `+` and `+=`_
# [PythonStyleGuide#TODO_style TODO style]: `TODO(username):` _use a consistent style_
# [PythonStyleGuide#Imports_grouping_and_order Imports grouping, order and sorting]: _One per line, grouped and ordered by packages, sorted alphabetically_
# [PythonStyleGuide#Statements Statements]: _One per line, avoid [PythonStyleGuide#Semicolons Semicolons]_
# [PythonStyleGuide#Access_control Access control]: _`foo` if lightweight, `GetFoo()` and `SetFoo()` otherwise_
# [PythonStyleGuide#Naming Naming]: _`foo_bar.py` not `foo-bar.py`,_ *some differ from [http://www.python.org/dev/peps/pep-0008/ PEP8]*
# [PythonStyleGuide#Main Main]: `if __name__ == '__main__':`
# [PythonStyleGuide#Conclusion Conclusion]: _Look at what's around you!_
= Python Language Rules =
== Imports ==
*What it is:* Mechanism for making use of code between modules.
*Pros:* Very flexible relative to other languages. Packages, modules, classes,
functions, and fields all may be imported.
*Cons:* `from foo import *` or `import bar.baz.Spar` is very nasty and can
lead to serious maintenance issues because it makes it hard to find module
dependencies.
*Decision:* Import only modules. Use `as` to resolve name conflicts if
necessary. Use `from` for all imports below top-level. Use only absolute
imports; do not use relative imports. Do not import packages, classes,
functions, fields, or anything that is not a module.
{{{
import os
import sys
from models.graphics import views as graphics_views
from models.sounds import views as sounds_views
from sound.effects import echo
...
echo.echofilter(input, output, delay=0.7, atten=4)
}}}
Even if the module is in the same package, do not directly import the module
without the full package name. This might cause the package to be imported
twice and have unintended side effects.
== Function Arguments ==
*What they are:* Arguments passed to functions either by position or by name.
*Pros:* Passing arguments always by name can help code be self-documenting.
*Cons:* Passing positional arguments by name can make a call site appear as
though all arguments are optional. Passing keyword arguments by position can
make a call site appear as though all arguments are required.
*Decision:* Always pass positional arguments positionally and keyword
arguments by keyword.
== Threading ==
Melange as of January 2013 is not thread-safe. Work to support thread safety
is happening as part of [https://code.google.com/p/soc/issues/detail?id=1596
issue 1596], but for now avoid threading.
= Python Style Rules =
== Semicolons ==
Do not terminate your lines with semi-colons and do not use semi-colons
to put two commands on the same line.
== Line length ==
Maximum line length is 80 characters.
*Exception:* lines importing modules may end up longer than 80 characters.
Make use of Python's implicit line joining inside parentheses, brackets
and braces. If necessary, you can add an extra pair of parentheses around
an expression.
*Yes:*
{{{
fooBar(self, width, height, color='black', design=None, x='foo',
emphasis=None, highlight=0)
if ((width == 0) and (height == 0)
and (color == 'red') and (emphasis == 'strong')):
}}}
When a literal string won't fit on a single line, use parentheses for
implicit line joining.
{{{
x = ('This will build a very long long '
'long long long long long long string')
}}}
Make note of the indentation of the elements in the line continuation
examples above; see the
[PythonStyleGuide#Indentation indentation section] for explanation.
== Parentheses ==
Use parentheses sparingly. Do not use them:
* in `return` statements
* in conditional statements unless using parentheses for implied line continuation (see above)
* around tuples, unless they are necessary syntactically or for clarity
It is, however, fine to use parentheses:
* for implied line continuation
* around sub-expressions in a larger expression (including the sub-expressions that are part of a conditional statement)
In fact, parentheses around sub-expressions is preferred to relying solely
on operator precedence.
*Yes:*
{{{
if foo:
while x:
if x and y:
if not x:
if (x < 3) and (not y):
return foo
for x, y in dict.items():
x, (y, z) = funcThatReturnsNestedTuples()
}}}
*No:*
{{{
if (x):
while (x):
if not(x):
if ((x < 3) and (not y)):
return (foo)
for (x, y) in dict.items():
(x, (y, z)) = funcThatReturnsNestedTuples()
}}}
== Indentation ==
_Note that this differs from
[http://www.python.org/dev/peps/pep-0008/ PEP8]
and instead follows the original Google Python Style guide from which
this style guide originated._
Indent your code blocks with 2 spaces. Never use tabs or mix tabs and
spaces. In cases of implied line continuation, you should align wrapped
elements either vertically, as per the examples in the line length
section; or using a hanging indent of *4* spaces (not 2, so as not to be
confused with an immediately-following nested block), in which case there
should be no argument on the first line.
*Yes:*
{{{
# Aligned with opening delimiter
foo = longFunctionName(var_one, var_two,
var_three, var_four)
# 4-space hanging indent; nothing on first line
foo = longFunctionName(
var_one, var_two, var_three,
var_four)
}}}
*No:*
{{{
# Stuff on first line forbidden
foo = longFunctionName(var_one, var_two,
var_three, var_four)
# 2-space hanging indent forbidden
foo = longFunctionName(
var_one, var_two, var_three,
var_four)
}}}
== Blank lines ==
Two blank lines between top-level definitions, be they function or class
definitions. One blank line between method definitions and between the
class line and the first method. One blank line between
[PythonStyleGuide#Doc_strings `__doc__` strings] and the code that follows
them. Use single blank lines as you judge appropriate within functions or
methods.
Always have a single blank line at the end of the file, this suppresses the "\ No newline at end of file" message that is generated by most diff tools.
== Whitespace ==
No whitespace inside parentheses, brackets or braces.
*Yes:* `spam(ham[1], {eggs: 2}, [])`
*No:* `spam( ham[ 1 ], { eggs: 2 }, [ ] )`
No whitespace before a comma, semicolon, or colon. _Do_ use whitespace
after a comma, semicolon, or colon except at the end of the line.
*Yes:*
{{{
if x == 4:
print x, y
x, y = y, x
}}}
*No:*
{{{
if x == 4 :
print x , y
x , y = y , x
}}}
No whitespace before the open paren/bracket that starts an argument
list, indexing or slicing.
*Yes:* `spam(1)`
*No:* `spam (1)`
*Yes:* `dict['key'] = list[index]`
*No:* `dict ['key'] = list [index]`
Surround binary operators with a single space on either side for
assignment (`=`), comparisons (`==`, `<`, `>`, `!=`, `<>`, `<=`, `>=`,
`in`, `not in`, `is`, `is not`), and booleans (`and`, `or`, `not`). Use
your better judgment for the insertion of spaces around arithmetic
operators but always be consistent about whitespace on either side of
a binary operator.
*Yes:* `x == 1`
*No:* `x<1`
Don't use spaces around the '`=`' sign when used to indicate a keyword
argument or a default parameter value.
*Yes:* `def Complex(real, imag=0.0): return Magic(r=real, i=imag)`
*No:* `def Complex(real, imag = 0.0): return Magic(r = real, i = imag)`
=== Python Interpreter ==
Modules which are not intended for direct execution (and which have no effect if executed) should not include a shebang line.
Modules intended for direct execution should begin with a shebang line specifying the Python interpreter used to execute the program, most likely `#!/usr/bin/python` or `#!/usr/bin/python2.7`.
== Comments ==
=== Doc strings ===
Python has a unique commenting style using `__doc__` strings. A `__doc__`
string is a string that is the first statement in a package, module,
class or function. These strings can be extracted automatically through
the `__doc__()` member of the object and are used by `pydoc`. (Try
running `pydoc` on your module to see how it looks.) Our convention for
`__doc__` strings is to use the three double-quote format for strings.
A `__doc__` string should be organized as a summary line (one physical
line, [PythonStyleGuide#Line_length 80 characters or less]) terminated
by a period, followed by a blank line, followed by the rest of the
`__doc__` string starting at the same cursor position as the first quote
of the first line. There are more formatting guidelines for `__doc__`
strings below.
=== Modules ===
Every file should contain a block comment with a copyright notice and
license statement at the top of the file.
==== Copyright and license notices ====
{{{
#!/usr/bin/python2.5
#
# Copyright [current year] the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
}}}
This should be followed by a `__doc__` string describing the contents
of the module. Author information is then included after this string.
If an optional email is provided, then the entire author string
added to the `__authors__` list needs to be an
[http://www.ietf.org/rfc/rfc2821.txt RFC 2821 compliant] email address.
==== Module header and authors ====
{{{
"""A one line summary of the module or script, terminated by a period.
Leave one blank line. The rest of this __doc__ string should contain an
overall description of the module or script. Optionally, it may also
contain a brief description of exported classes and functions.
ClassFoo: One line summary.
functionBar(): One line summary.
"""
__authors__ = [
# alphabetical order by last name, please
'"John Smith" <johnsmith@example.com>',
'"Joe Paranoid" <joeisgone@example.com>', # email address is required
]
}}}
If a new contributor is not yet present in the
[http://soc.googlecode.com/svn/trunk/AUTHORS AUTHORS file], that contributor
should be added to that file as part of the first commit from that
contributor.
=== Functions and Methods ===
Any function or method which is not both obvious and very short needs
a `__doc__` string. Additionally, any externally accessible function
or method regardless of length or simplicity needs a `__doc__` string.
The `__doc__` string should include what the function does and have
detailed descriptions of the input ("`Args:`") and output
("`Returns:`", "`Raises:`", or "`Yields:`"). The `__doc__` string
should give enough information to write a call to the function without
looking at a single line of the function's code. The `__doc__` string
should specify the expected types where specific types are required,
and it should mention any default argument values. A "`Raises:`" section
should list all exceptions that can be raised by the function. The
`__doc__` string for generator functions should use "`Yields:`" rather
than "`Returns:`".
The function or method `__doc__` string should not, generally, describe
implementation details unless it's some complicated algorithm. For
tricky code, block/inline comments within the code are more appropriate.
{{{
def fetchRows(table, keys):
"""Fetch rows from a table.
Args:
table: An open table.Table instance.
keys: A sequence of strings representing the key of each table
row to fetch.
Returns:
A dict mapping keys to the corresponding table row data
fetched. Each row is represented as a tuple of strings. For
example:
{'Serak': ('Rigel VII', 'Preparer'),
'Zim': ('Irk', 'Invader'),
'Lrrr': ('Omicron Persei 8', 'Emperor')}
If a key from the keys argument is missing from the dictionary,
then that row was not found in the table.
Raises:
IOError: An error occurred accessing the table.Table object.
"""
pass
}}}
=== Classes ===
Classes should have a `__doc__` string below the class definition
describing the class. If your class has public attributes they should
be documented here in an `Attributes:` section.
{{{
class SampleClass(object):
"""Summary of class here.
Longer class information....
Longer class information....
Attributes:
likes_spam: A boolean indicating if we like SPAM or not.
eggs: An integer count of the eggs we have laid.
"""
def __init__(self, likes_spam=False):
"""Inits SampleClass with blah.
Args:
likes_spam: Initial indication of if the SampleClass
instance likes SPAM or not (default is False)
"""
self.likes_spam = likes_spam
self.eggs = 0
def publicMethod(self):
"""Perform operation blah."""
pass
}}}
=== Block and Inline Comments ===
The final place to have comments is in tricky parts of the code. If
you're going to have to explain it at the next code review, you should
comment it now. Complicated operations get a few lines of comments
before the operations commence. Non-obvious ones get comments at the
end of the line.
{{{
# We use a weighted dictionary search to find out where i is in
# the array. We extrapolate position based on the largest number
# in the array and the array size and then do binary search to
# get the exact number.
if i & (i-1) == 0: # true iff i is a power of 2
}}}
These comments should be separated from the code to improve legibility.
Block comments should be preceded by a blank line. In general,
end-of-line comments should be at least 2 spaces away from the code. These
end-of-line comments _can_ be lined up if there are many in a row (or
in a function), but this is not required.
On the other hand, never describe the code. Assume the person reading
the code knows Python (though not what you're trying to do) better
than you do.
{{{
# BAD COMMENT: Now go through the b array and make sure whenever i occurs
# the next element is i+1
}}}
== Classes ==
If a class inherits from no other base classes, explicitly inherit from
`object`. This also applies to nested classes.
*No:*
{{{
class SampleClass:
pass
class OuterClass:
class InnerClass:
pass
}}}
*Yes:*
{{{
class SampleClass(object):
pass
class OuterClass(object):
class InnerClass(object):
pass
class ChildClass(ParentClass):
"""Explicitly inherits from another class already."""
pass
}}}
Inheriting from `object` is needed to make properties work properly, and
it will protect our code from one particular flavor of things that might
break once we switch to Python 3000. It also defines special methods that
implement the default semantics of objects including `__new__`,
`__init__`, `__delattr__`, `__getattribute__`, `__setattr__`, `__hash__`,
`__repr__`, and `__str__`.
== Strings ==
Use the `%` operator for formatting strings, even when the parameters
are all strings. Use your best judgement to decide between `+` and `%`
though.
*No:*
{{{
x = '%s%s' % (a, b) # use + in this case
x = imperative + ', ' + expletive + '!'
x = 'name: ' + name + '; score: ' + str(n)
}}}
*Yes:*
{{{
x = a + b
x = '%s, %s!' % (imperative, expletive)
x = 'name: %s; score: %d' % (name, n)
}}}
Avoid using the `+` and `+=` operators to accumulate a string within
a loop. Since strings are immutable, this creates unnecessary temporary
objects and results in quadratic rather than linear running time.
Instead, add each sub-string to a `list` and `''.join()` that list after
the loop terminates.
*No:*
{{{
employee_table = '<table>'
for last_name, first_name in employee_list:
employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
employee_table += '</table>'
}}}
*Yes:*
{{{
items = ['<table>']
for last_name, first_name in employee_list:
items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
items.append('</table>')
employee_table = ''.join(items)
}}}
Use `"""` for multi-line strings rather than `'''`. Note, however, that
it is often cleaner to use implicit line joining since multi-line
strings do not flow with the indentation of the rest of the program:
*No:*
{{{
print """This is pretty ugly.
Don't do this.
"""
}}}
*Yes:*
{{{
print ("This is much nicer.\n"
"Do it this way.\n")
}}}
== TODO style ==
Use `TODO` comments for code that is temporary, a short-term solution,
or good-enough but not perfect.
`TODO`s should include the string `TODO` in all caps, followed by your
`username` in parentheses: `TODO(username)`. A colon is optional. The
main purpose is to have a consistent `TODO` format searchable by `username`.
{{{
# TODO(someuser): Use a "*" here for concatenation operator.
# TODO(anotheruser) change this to use relations.
}}}
If your `TODO` is of the form "At a future date do something" make sure
that you either include a very specific date ("Fix by November 2008")
or a very specific event ("Remove this code after the Foo entities in
the datastore have all added the new fubar property.").
== Imports grouping and order ==
Imports should be on separate lines, e.g.:
*Yes:*
{{{
import os
import sys
}}}
*No:*
{{{
import sys, os
}}}
Imports are always put at the top of the file, just after any module
comments and `__doc__` strings and before module globals and constants.
Imports should be grouped with the order being most generic to least
generic:
* standard library imports
* third-party library imports
* Google App Engine imports
* django framework imports
* SoC framework imports
* SoC-based module imports
* application specific imports
Sorting should be done alphabetically.
{{{
import a_standard
import b_standard
import a_third_party
import b_third_party
from a_soc import f
from a_soc import g
from b_soc import d
}}}
== Statements ==
Generally only one statement per line. However, you may put the result
of a test on the same line as the test only if the entire statement
fits on one line. In particular, you can never do so with `try/except`
since the `try` and `except` can't both fit on the same line, and you
can only do so with an `if` if there is no `else`.
*Yes:*
{{{
if foo: fuBar(foo)
}}}
*No:*
{{{
if foo: fuBar(foo)
else: fuBaz(foo)
try: fuBar(foo)
except ValueError: fuBaz(foo)
try:
fuBar(foo)
except ValueError: fuBaz(foo)
}}}
== Access control ==
If an accessor function would be trivial you should use public variables
instead of accessor functions to avoid the extra cost of function calls
in Python. When more functionality is added you can use property to keep
the syntax consistent.
On the other hand, if access is more complex, or the cost of accessing
the variable is significant, you should use function calls (following
the [PythonStyleGuide#Naming Naming guidelines]) such as `getFoo()` and
`setFoo()`. If the past behavior allowed access through a property, do
not bind the new accessor functions to the property. Any code still
attempting to access the variable by the old method should break visibly
so callers are made aware of the change in complexity.
== Naming ==
=== Names to avoid ===
* single character names, except for counters or iterators
* dashes (`-`) in any package/module name
* `__double_leading_and_trailing_underscore__` names (reserved by Python)
=== Naming convention ===
_Note that some naming conventions differ from
[http://www.python.org/dev/peps/pep-0008/ PEP8]
and instead follow the original Google Python Style guide from which
this style guide originated._
* "Internal" means internal to a module or protected or private within a class.
* Prepending a single underscore (`_`) has some support for protecting module variables and functions (not included with `import * from`).
* Prepending a double underscore (`__`) to an instance variable or method effectively serves to make the variable or method private to its class (using name mangling).
* Place related classes and top-level functions together in a module. Unlike Java, there is no need to limit yourself to one class per module. However, make sure the classes and top-level functions in the same module have [http://en.wikipedia.org/wiki/Cohesion_(computer_science) high cohesion].
* Use `CapWords` for class names, but `lower_with_under.py` for module names.
=== Naming examples ===
|| *Type* || *Public* || *Internal* ||
|| _Packages_ || `lower_with_under` || ||
|| _Modules_ || `lower_with_under` || `_lower_with_under` ||
|| _Classes_ || `CapWords` || `_CapWords` ||
|| _Exceptions_ || `CapWords` || ||
|| _Functions_ || `firstLowerCapWords()` || `_firstLowerCapWords()` ||
|| _Global/Class Constants_ || `CAPS_WITH_UNDER` || `_CAPS_WITH_UNDER` ||
|| _Global/Class Variables_ || `lower_with_under` || `_lower_with_under` ||
|| _Instance Variables_ || `lower_with_under` || `_lower_with_under` _(protected)_ or `__lower_with_under` _(private)_ ||
|| _Method Names_^*^ || `firstLowerCapWords()` || `_firstLowerCapWords()` _(protected)_ or `__firstLowerCapWords()` _(private)_ ||
|| _Function/Method Parameters_ || `lower_with_under` || ||
|| _Local Variables_ || `lower_with_under` || ||
^*^ Consider just using
[PythonStyleGuide#Access_control direct access to public attributes]
in preference to getters and setters, as function calls are expensive
in Python, and `property` can be used later to turn attribute access into a
function call without changing the access syntax.
== Main ==
Every Python source file should be importable. In Python, `pychecker`,
`pydoc`, and unit tests require modules to be importable. Your code
should always check `if __name__ == '__main__':` before executing your
main program so that the main program is not executed when the module
is imported. The capitalization of `main()` is intentionally inconsistent
with the rest of the naming conventions, which would suggest `Main()`.
{{{
if __name__ == '__main__':
# parse arguments
main()
}}}
All code at the top level will be executed when the module is imported.
Be careful not to call functions, create objects, or perform other
operations that should not be executed when the file is being `pycheck`ed
or `pydoc`ed.
== Conclusion ==
_BE CONSISTENT._
If you are editing code, take a few minutes to look at the code around
you and determine its style. If spaces are used around the `if` clauses,
you should, too. If the comments have little boxes of stars around them,
make your comments have little boxes of stars around them, too.
The point of having style guidelines is to have a common vocabulary
of coding so people can concentrate on what you are saying rather than
on how you are saying it. We present global style rules here so people
know the vocabulary, but local style is also important. If code you add
to a file looks drastically different from the existing code around it,
it throws readers out of their rhythm when they go to read it. Try to
avoid this.
----
_Copyright 2008 Google Inc._
_This work is licensed under a_
[http://soc.googlecode.com/svn/wiki/html/licenses/cc-by-attribution-2_5.html Creative Commons Attribution 2.5 License].
[http://creativecommons.org/licenses/by/2.5/ http://soc.googlecode.com/svn/wiki/html/licenses/cc-by-2_5-88x31.png]