Python coding rules

The Python coding rules have been inspired from the Style Guide for Python Code PEP 8. You can check whether you code complies to these rules using the pep8 tool that you should run on your Python code before committing.

Dependencies

  • All ctools code must be compatible with Python 2.3 or higher, and Python 3.0 and higher.

  • ctools should be importable with no dependencies other than the Python Standard Library.

Code lay-out

  • Put imports on separate lines, e.g.:

    Yes: import os
         import sys
    
    No:  import sys, os
    
  • Put imports at the top of the file, just after any module comments and docstrings, and before module globals and constants.

  • Use absolute imports, e.g.:

    import mypkg.sibling
    from mypkg import sibling
    from mypkg.sibling import example
    

String quotes

  • Use single quotes for strings so that the string can contain double quote characters which avoid backslashes, e.g.:

    Yes: print('The Parameter "infile" is missing')
    
    No:  print("The Parameter \"infile\" is missing")
    

Whitespace in Expressions and Statements

  • Do not put extraneous whitespace immediately inside parentheses, brackets or braces, e.g.:

    Yes: spam(ham[1], {eggs: 2})
    
    No:  spam( ham[ 1 ], { eggs: 2 } )
    
  • Do not put extraneous whitespace immediately before a comma, semicolon, or colon, e.g.:

    Yes: if x == 4: print x, y; x, y = y, x
    
    No:  if x == 4 : print x , y ; x , y = y , x
    
  • Do not put extraneous whitespace immediately before the open parenthesis that starts the argument list of a function call, e.g.:

    Yes: spam(1)
    
    No:  spam (1)
    
  • Do not put extraneous whitespace immediately before the open parenthesis that starts an indexing or slicing, e.g.:

    Yes: dct['key'] = lst[index]
    
    No:  dct ['key'] = lst [index]
    

Comments

  • Always make a priority of keeping the comments up-to-date when the code changes! Comments that contradict the code are worse than no comments.

  • Comments should be complete sentences. If a comment is a phrase or sentence, its first word should be capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of identifiers!).

  • If a comment is short, the period at the end can be omitted. Block comments generally consist of one or more paragraphs built out of complete sentences, and each sentence should end in a period.

  • Follow Strunk & White. Make every word tell. Omit needless words. Use active voice. Use parallel construction on parallel concepts.

  • Block comments generally apply to some (or all) code that follows them, and are indented to the same level as that code. Each line of a block comment starts with a # and a single space (unless it is indented text inside the comment). Paragraphs inside a block comment are separated by a line containing a single #, e.g.:

    # Compute the energy boundaries.
    #
    # The energy boundaries are computed from the lower and upper energy
    # thresholds that are stored in the effective area components of the
    # response functions.
    ebounds = gammalib.GEbounds()
    ...
    
  • Use inline comments sparingly. Use meaningful variable names instead. But sometimes, inline comments can be useful, e.g.:

    x = x + 1  # Compensate for border
    

User Documentation

  • Write documentation strings (a.k.a. docstrings) for all modules, functions, classes, and public methods. Read PEP 257 to learn about the general Python conventions for writing good documentation strings.

  • The docstring conventions follow the common standards developed by NumPy and SciPy and that are also adopted by AstroPy. This docstring standard uses re-structured text (reST) syntax and is rendered using Sphinx. While a rich set of markup is available, limit yourself to a very basic subset, in order to provide docstrings that are easy to read on text-only terminals.

  • Format each docstring as follows (sections can be omitted if they do not apply, for example you do not need to specify that nothing is returned):

    """
    Extract calibrations for a mission from a calibration database.
    
    .. note:: Deprecated in ctools 1.1
              The `instrument` string will be removed in ctools 2.0 because
              it's in fact not used by the method.
    
    The method extract calibration names from a calibration database by
    scanning all index files.
    
    Parameters
    ----------
    caldb :  `~gammalib.GCaldb`
        Calibration database.
    instrument : str
        Instrument names.
    debug : bool, optional
        Turn on debugging if needed.
    
    Returns
    -------
    missions : list of str
        List of mission names.
    
    Raises
    ------
    ValueError
        If `instrument` is not recognised as among the ones implemented.
    
    See Also
    --------
    irfs : Return all irfs for a given calibration.
    
    Notes
    -----
    See https//my.site.edu for documentation. The method computes
    
    .. math:: X(e^{j\omega } ) = x(n)e^{ - j\omega n}
    
    where the value of :math:`\omega` is larger than 5.
    
    References
    ----------
    .. [1] J. Public, "The art of programming", Computers & Geosciences,
       vol. 22, pp. 585-588, 1996.
    
    Examples
    --------
    >>> calibrations(caldb, "CTA", debug=False)
    ['Prod2', 'Prod3']
    """
    
  • For the parameter types, be as precise as possible. Below are a few examples of parameters and their types.

    Parameters
    ----------
    filename : str
    copy : bool
    dtype : data-type
    iterable : iterable object
    shape : int or tuple of int
    files : list of str
    
  • For classes, use the same sections as outlined above. The constructor (__init__) should also be documented here, the Parameters section of the docstring details the constructors parameters. An Attributes section, located below the Parameters section, may be used to describe class variables:

    Attributes
    ----------
    x : float
        The X coordinate.
    y : float
        The Y coordinate.
    
  • In general, it is not necessary to list class methods. In some cases, however, a class may have many methods, and then it becomes useful to have an additional Methods section:

    Methods
    -------
    read(filename)
        Read a table from a file
    sort(column, order='ascending')
        Sort by column
    

Naming Conventions

  • Use short lowercase abbreviate words for cscripts, e.g.:

    cspull
    csspec
    cslightcrv
    
  • Use lower_case_with_underscores for functions, methods, and variables.

  • Use self for the first argument to instance methods, e.g.:

    def __init__(self, name):
       self._name = name
    
  • Use one leading underscore for private methods and instance variables, e.g.:

    self._get_energy_boundaries()
    self._has_ebounds = True
    
  • Always decide whether a class’s methods and instance variables (collectively: “attributes”) should be public or private. If in doubt, choose private; it’s easier to make it public later than to make a public attribute private.