Difference between revisions of "Programming guidelines"

From Gramps
Jump to: navigation, search
m (Coding style)
(18 intermediate revisions by 10 users not shown)
Line 1: Line 1:
[[Category:Developers/General]]
+
In a multi-programmer environment, it is important to follow common coding guidelines to make sure the code remains maintainable.
  
As more and more people start editing the code, we need to establish a few guidelines to make sure the code remains maintainable.
+
== Coding style ==
  
* Write [http://www.python.org/dev/peps/pep-0008/ PEP 8] compatible code. This is important to have a consistent, readable codebase.
+
=== PEP8 ===
 +
* Write [http://www.python.org/dev/peps/pep-0008/ PEP 8] compatible code! This is important to have a consistent, readable codebase.
 +
** it is not explicit in PEP8, but we like a space after a comma
  
* Do not use tabs. Use whitespace. If you use an editor with tabs, go into the preferences, and change the tab behavior to a number of spaces. In GRAMPS we use 4 spaces for indentation.
+
=== Tabs ===
 +
* Do not use TABs. Use space characters. In GRAMPS we use 4 spaces for indentation. This does not mean you must set your TAB stops to 4. TABs and indents are not the same thing. Most editors have a configuration option to set indentation and TAB stops. Be careful to just set the '''indentation''' to 4, which automatically means it has to be '''spaces'''. (TABs are still necessary, in Makefiles for example, and they '''have to''' be equivalent to 8 spaces, '''always'''.) To summarize:
 +
** uses spaces, no TABs
 +
** indentation is 4
 +
** TAB stops (if any) are at position 9,17,25,... (first column is 1)
  
* New modules should follow the GRAMPS convention of module structuring:
+
=== Members names ===
# GRAMPS header (programm name, copyright owner, license);
+
* Private class functions (functions that cannot be called outside the class) should be preceded with two underscores.
# SVN $Id$;
+
* Protected functions (functions that can only be called by the class or derived classes) should be preceded with one underscore.
# Module docstring;
+
<code>
# Author and revision (using SVN $Revision$);
+
  def __private_function(self):
# Imports in the following order, separated with proper headers: Python modules, Logging, Gnome/Gtk modules, GRAMPS modules;
+
      pass
# Constants with header;
+
 
# Global variables, module functions, classes, etc.
+
  def _protected_function(self):
 +
      pass
 +
</code>
  
<pre>
+
=== Callbacks ===
  #
+
  # Gramps - a GTK+/GNOME based genealogy program
+
  #
+
  # Copyright (C) 2000-2007  <Copyright Owner>
+
  #
+
  # This program is free software; you can redistribute it and/or modify
+
  # it under the terms of the GNU General Public License as published by
+
  # the Free Software Foundation; either version 2 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 General Public License for more details.
+
  #
+
  # You should have received a copy of the GNU General Public License
+
  # along with this program; if not, write to the Free Software
+
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
  #
+
  
  # $Id$
+
Names of callbacks should be prefixed by 'cb_'.  For example, <code>cb_my_callback</code>.
  
  """Module docstring.
+
<code>pylint</code> does not check that arguments are used when methods are named in this way.  This is useful to avoid the <code>pylint</code> warning:  'W0613: Unused argument <arg>'.
  """
+
  
  __author__  = "Your Name"
+
=== Imports ===
  __revision__ = "$Revision$"
+
The top module is called gramps, and it has following submodules:
 +
# gen
 +
# cli
 +
# gui
 +
# plugins
 +
The other dirs should not contain code, or are for testing.
  
  #------------------------------------------------------------------------
+
Within a submodule, only relative imports are allowed of the own submodule (so starting with . or with a module of the own directory), and absolute imports of other submodules (so starting with <code>gramps.</code>)
  #
+
  # Python modules
+
  #
+
  #------------------------------------------------------------------------
+
  import os
+
  
  #------------------------------------------------------------------------
+
'''Important''': files in the gen submodule are '''not''' allowed to import files from the other submodules. So <code>gen</code> should be self-contained.
  #
+
  # Set up logging
+
  #
+
  #------------------------------------------------------------------------
+
  import logging
+
  log = logging.getLogger(".NameOfYourModule")
+
  
  #-------------------------------------------------------------------------
+
'''Important 2''': current code does not satisfy this rule yet, should be done by end of 2012
  #
+
  # Gnome/GTK modules
+
  #
+
  #-------------------------------------------------------------------------
+
  import gtk
+
  
  #------------------------------------------------------------------------
+
== Class headers ==
  #
+
* Each class should have a simple header to help mark it in the file. This is not used for documentation - it is used to help find the class when multiple classes exist in the same file.
  # Gramps modules
+
  #
+
  #------------------------------------------------------------------------
+
  import RelLib
+
 
+
  #------------------------------------------------------------------------
+
  #
+
  # Constants
+
  #
+
  #------------------------------------------------------------------------
+
 
+
  # brief description of each constant
+
  TEST_VALUE = 72.0
+
 
+
  # etc.
+
</pre>
+
 
+
* Class headers. Each class should have a simple header to help mark it in the file. This is not used for documentation - it is used to help find the class when multiple classes exist in the same file.
+
 
<code>
 
<code>
 
   #------------------------------------------------------------
 
   #------------------------------------------------------------
Line 94: Line 53:
 
   #------------------------------------------------------------
 
   #------------------------------------------------------------
 
</code>
 
</code>
* Docstrings. Python provides a docstrings to document classes and functions. If the class is a class used by others (such as the RelLib classes), the docstrings should follow the [http://epydoc.sourceforge.net/manual-epytext.html epydoc] format. This allows us to extract [http://gramps-project.org/api3 API] documentation. Classes that are not core reusable classes do not have to follow this format, but should be documented using docstrings.
+
 
 +
== Docstrings ==
 +
* Python provides a docstrings to document classes and functions. If the class is a class used by others (such as the [http://www.gramps-project.org/docs/gen/gen_lib.html#module-gen.lib gen lib] classes), the docstrings should follow the restructuredtext ([http://docutils.sourceforge.net/docs/user/rst/quickstart.html#structure rst]) format. This allows us to extract [http://www.gramps-project.org/docs/ API] documentation using sphinx.  
 +
 
 +
* Apart from adding doc strings to classes and functions, also the api generating rst files must be edited so as to extract the documentation. These files are in the [http://gramps.svn.sourceforge.net/viewvc/gramps/trunk/docs/ docs directory], for info read the [http://gramps.svn.sourceforge.net/viewvc/gramps/trunk/docs/README.txt?view=markup README.txt] file.
 +
 
 +
:More info
 +
:* [http://sphinx.pocoo.org/markup/desc.html sphinx for python]
 +
:* [http://sphinx.pocoo.org/rest.html doc with sphinx]
 +
 
 +
Classes that are not core reusable classes do not have to follow this format (although we encourage you do), but should be documented using docstrings.
 
<code>
 
<code>
 
   class MyClass:
 
   class MyClass:
Line 107: Line 76:
 
           pass
 
           pass
 
</code>
 
</code>
* Private class functions (functions that cannot be called outside the class) should be preceded with two underscores. Protected functions (functions that can only be called by the class or derived classes) should be preceded with one underscore.
+
 
<code>
+
== Pylint ==
  def __private_function(self):
+
* Run <code>pylint</code> on your code before checking in.
      pass
+
* New files shall have a Pylint score of 9 or higher. New files will not be accepted if they have a Pylint score lower than 9.
 
+
* Any changes to existing files with a Pylint score lower than 9 shall not reduce the Pylint score. It is expected that over time, this policy will cause all files to eventually have a score of 9 or higher.
  def _protected_function(self):
+
 
      pass
+
Note that you must run <code>pylint</code> in the <code>src</code> directory. If import errors still occur, add a PYTHONPATH. Example usage:
</code>
+
  me@laptop:~/programs/trunk/src$ PYTHONPATH=plugins/lib/ pylint --include-ids=y --reports=y plugins/mapservices/googlemap.py
* Run <code>pylint</code> on your code before checking in, and use the output to reasonably clean up the code. Note that you must run <code>pylint</code> in the <code>src</code> directory.
+
Set reports to n to have less output. Info on the codes can be found here: [http://www.logilab.org/card/pylintfeatures]
 +
 
 +
== Best practices ==
 +
* Always develop with [[Coding_for_translation|language translation]] in mind
 +
 
 +
* Reduce dependencies (imports) between files.
 +
 
 +
* Think on [[Accessibility]].
 +
 
 +
[[Category:Developers/General]]
 +
[[Category:Developers/Quality Assurance]]

Revision as of 12:28, 30 September 2012

In a multi-programmer environment, it is important to follow common coding guidelines to make sure the code remains maintainable.

Coding style

PEP8

  • Write PEP 8 compatible code! This is important to have a consistent, readable codebase.
    • it is not explicit in PEP8, but we like a space after a comma

Tabs

  • Do not use TABs. Use space characters. In GRAMPS we use 4 spaces for indentation. This does not mean you must set your TAB stops to 4. TABs and indents are not the same thing. Most editors have a configuration option to set indentation and TAB stops. Be careful to just set the indentation to 4, which automatically means it has to be spaces. (TABs are still necessary, in Makefiles for example, and they have to be equivalent to 8 spaces, always.) To summarize:
    • uses spaces, no TABs
    • indentation is 4
    • TAB stops (if any) are at position 9,17,25,... (first column is 1)

Members names

  • Private class functions (functions that cannot be called outside the class) should be preceded with two underscores.
  • Protected functions (functions that can only be called by the class or derived classes) should be preceded with one underscore.

 def __private_function(self):
     pass
  
 def _protected_function(self):
     pass

Callbacks

Names of callbacks should be prefixed by 'cb_'. For example, cb_my_callback.

pylint does not check that arguments are used when methods are named in this way. This is useful to avoid the pylint warning: 'W0613: Unused argument <arg>'.

Imports

The top module is called gramps, and it has following submodules:

  1. gen
  2. cli
  3. gui
  4. plugins

The other dirs should not contain code, or are for testing.

Within a submodule, only relative imports are allowed of the own submodule (so starting with . or with a module of the own directory), and absolute imports of other submodules (so starting with gramps.)

Important: files in the gen submodule are not allowed to import files from the other submodules. So gen should be self-contained.

Important 2: current code does not satisfy this rule yet, should be done by end of 2012

Class headers

  • Each class should have a simple header to help mark it in the file. This is not used for documentation - it is used to help find the class when multiple classes exist in the same file.

 #------------------------------------------------------------
 #
 # MyClass
 #
 #------------------------------------------------------------

Docstrings

  • Python provides a docstrings to document classes and functions. If the class is a class used by others (such as the gen lib classes), the docstrings should follow the restructuredtext (rst) format. This allows us to extract API documentation using sphinx.
  • Apart from adding doc strings to classes and functions, also the api generating rst files must be edited so as to extract the documentation. These files are in the docs directory, for info read the README.txt file.
More info

Classes that are not core reusable classes do not have to follow this format (although we encourage you do), but should be documented using docstrings.

 class MyClass:
     """
     MyClass is a sample class.
     """
    
     def my_function(self):
         """
         The my_function task serves no purpose whatsoever.
         """
         pass

Pylint

  • Run pylint on your code before checking in.
  • New files shall have a Pylint score of 9 or higher. New files will not be accepted if they have a Pylint score lower than 9.
  • Any changes to existing files with a Pylint score lower than 9 shall not reduce the Pylint score. It is expected that over time, this policy will cause all files to eventually have a score of 9 or higher.

Note that you must run pylint in the src directory. If import errors still occur, add a PYTHONPATH. Example usage:

 me@laptop:~/programs/trunk/src$ PYTHONPATH=plugins/lib/ pylint --include-ids=y --reports=y plugins/mapservices/googlemap.py

Set reports to n to have less output. Info on the codes can be found here: [1]

Best practices

  • Reduce dependencies (imports) between files.