# ObsPy Coding Style Guide

Like most Python projects, we try to adhere to PEP 8 (Style Guide for Python Code) and PEP 257 (Docstring Conventions) with the modifications documented here. Be sure to read all documents if you intend to contribute code to ObsPy.

We rely on flake8 for code style checks, it can be installed using `conda install` or `pip install` and can also be used to set up git pre-commit hooks. That way, `flake8` will immediately complain about problems with the coding style and the changes staged for committing can be adapted accordingly (even with git commit hooks installed, they can be ignored on a per-commit basis using `git commit -n`).

## Import Conventions

Like the Python projects NumPy, SciPy and matplotlib, we try to improve readability of the code by importing the following modules in an unified manner:

    >>> import numpy as np
    >>> import matplotlib.pylab as plt

## 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

- Use meaningful variable/function/method names; these will help other people a lot when reading your code.
- Prepending a single underscore (`_`) means an object is “internal” / “private”, which means that it is not supposed to be used by end-users and the API might change internally without notice to users (in contrast to API changes in public objects which get handled with deprecation warnings for one release cycle).
- 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.
- Use `CamelCase` for class names, but `snake_case` for module names, variables and functions/methods.

Type | Public | Internal / Private
--- | --- | ---
Packages | `snake_case` |
Modules | `snake_case.py` | `_snake_case`
Classes / Exceptions | `CamelCase` | `_CamelCase`
Functions / Methods | `snake_case()` | `_snake_case()`
Variables / Attributes | `snake_case` | `_snake_case`
Constants | `CAPS_WITH_UNDER` | `_CAPS_WITH_UNDER`

## Doc Strings / Comments

- One-liner Doc Strings: both `"""` are in new lines

      def some_method():
          """
          This is a one line doc string.
          """
          print("test")

- Multiple line Doc Strings: both `"""` are in new lines - also you should try provide a meaningful one-liner description at the top, followed by two linebreaks with further text.

      def some_method():
          """
          This is just the short story.

          The long story is, this docstring would not have been able to fit in
          one line. Therefore we have to break lines.
          """
          print("test")

- Comments at the end of code lines should come after (at least) two spaces:

      x = x + 1  # Compensate for border

- Comments start with a single # followed by a single space. The same goes for multi-line block comments:

      # Compensate for border
      x = x + 1
      # The next line needs some more longish explanation which does not fit
      # on a single line.
      foobar = (foo + bar) ** 3 - 1

## Function/Method Definitions

In docstrings which annotate functions and methods, the following reStructuredText fields are recognized and formatted nicely:

`param`
  Description of a parameter.
`type`
  Type of a parameter.
`raises`, `raise`
  That (and when) a specific exception is raised.
`var`
  Description of a variable.
`returns`, `return`
  Description of the return value.
`rtype`
  Return type.

The field names must consist of one of these keywords and an argument (except for `returns` and `rtype`, which do not need an argument). This is best explained by an example:

    def format_exception(etype, value, tb, limit=None):
        """
        Format the exception with a traceback.

        :param etype: exception type
        :param value: exception value
        :param tb: traceback object
        :param limit: maximum number of stack frames to show
        :type limit: integer or None
        :rtype: list of strings
        :return: Traceback messages.
        """

which renders like this:

`format_exception`(etype, value, tb, limit=None)

  Format the exception with a traceback.

  Parameters: | etype exception type value exception value tb traceback object limit (integer or None) maximum number of stack frames to show
  Return type: | list of strings
  Returns: | Traceback messages.

## Reference Conventions

As with `numpy.ndarrays` or Python `lists`, we try to reduce the memory consumption by using references where ever possible. In the following example `a` is appended to `b` as reference, that is the reason why `b` get changed when we change `a`:

    >>> a = [1, 2, 3, 4]
    >>> b = [5, 6]
    >>> b.append(a)
    >>> a[0] = -99
    >>> print(b)
    [5, 6, [-99, 2, 3, 4]]

## Tests

- test methods names must start with `test_` followed by a mixedCase part

- Tests which are expected to fail, because there is a known/unfixed bug should be commented with an `XXX:` followed by an valid ticket number, e.g.

      def test_do_something():
          """
          XXX: This test does something.

          But fails badly. See ticket #number.
          """
          print("test")
          ...
          # XXX: here it fails
          ...

## Citations

References to publications (journal articles, books, etc.) should be properly reproducible. A bibtex entry in obspy/misc/docs/source/bibliography should be made for each single publication (ideally with an URL or DOI), using first author and year as article identifier:

    @article{Beyreuther2010,
    author = {Beyreuther, Moritz and Barsch, Robert and Krischer,
              Lion and Megies, Tobias and Behr, Yannik and Wassermann, Joachim},
    title = {ObsPy: A Python Toolbox for Seismology},
    volume = {81},
    number = {3},
    pages = {530-533},
    year = {May/June 2010},
    doi = {10.1785/gssrl.81.3.530},
    URL = {http://www.seismosoc.org/publications/SRL/SRL_81/srl_81-3_es/},
    eprint = {http://srl.geoscienceworld.org/content/81/3/530.full.pdf+html},
    journal = {Seismological Research Letters}
    }

This entry can then be referenced (using the bibtex article identifier) in docstrings in the source code with the following Sphinx syntax to be converted to a link to the bibliography section:

>     def some_function():
>         """
>         Function to do something.

>         See [Beyreuther2010]_ for details.
>         """
>         return None

## Miscellaneous

- Lines shouldn’t exceed a length of `79` characters. No, it’s not because we’re mainly using VT100 terminals while developing, rather because the diffs look nicer on short lines, especially in side-by-side mode.
- never use multiple statements on the same line, e.g. `if check: a = 0`.
- Prefer list comprehension to the built-in functions `filter()` and `map()` when appropriate.

### Thank you!

We would like to thank our contributors, whose efforts make this software what it is. These people have helped by writing code and documentation, and by testing. They have created and maintained this product, its associated libraries and applications, our build tools and our web sites.

#### Contributors

- Ackerley, Nick
- Allgeyer, Sebastien
- Ammon, Charles J.
- Antunes, Emanuel
- Arnarsson, Ólafur St.
- Bank, Markus
- Barsch, Robert
- Behr, Yannik
- Bernardi, Fabrizio
- Bernauer, Felix
- Bes de Berc, Maxime
- Beyreuther, Moritz
- Boltz, Shawn
- Bonaimé, Sébastien
- Carothers, Lloyd
- Chamberlain, Calum
- Chambers, Derrick
- Chen, Zhao
- Clark, Adam
- Danecek, Peter
- Egdorf, Sven
- Engels, Fabian
- Ermert, Laura
- Eulenfeld, Tom

- Fabbri, Tommaso
- Falco, Nicholas
- Fee, Jeremy
- Grellier, Clément
- Grunberg, Marc
- Hammer, Conny
- Heimann, Sebastian
- Heiniger, Lukas
- Hope, Gaute
- Igel, Heiner
- Inza, Adolfo
- Isken, Marius
- Ketchum, David
- Koymans, Mathijs
- Kremers, Simon
- Kress, Victor
- Krieger, Lars
- Krischer, Lion
- Kula, Damian
- Käufl, Paul
- Köhler, Andreas
- Lecocq, Thomas
- Leeman, John
- Legovini, Paride

- Lesage, Philippe
- Li, Yulin
- Lomax, Anthony
- Lopes, Rui L.
- MacCarthy, Jonathan
- Maggi, Alessia
- Martin, Henri
- Medlin, Andrew
- Megies, Tobias
- Meschede, Matthias
- Michelini, Alberto
- Miller, Nathaniel C.
- Morgenstern, Bernhard
- Nof, Ran Novitsky
- Panning, Mark P.
- Parker, Tom
- Pestourie, Romain
- Rapagnani, Giovanni
- Reyes, Celso
- Ringler, Adam
- Rothenhäusler, Nicolas
- Russo, Emiliano
- Sales de Andrade, Elliott

- Satriano, Claudio
- Saul, Joachim
- Scheingraber, Chris
- Schneider, Simon
- Sippl, Christian
- Snoke, Arthur
- Stange, Stefan
- Stähler, Simon C.
- Sullivan, Benjamin
- Tian, Dongdong
- Trabant, Chad
- Trani, Luca
- Uieda, Leonardo
- Walker, Andrew
- Walther, Marcus
- Wassermann, Joachim
- Williams, Mark C.
- Winkelman, Andrew
- Zaccarelli, Riccardo
- Zad, Seyed Kasra Hosseini
- Zhu, Lijun
- van Driel, Martin
- van Wijk, Kasper

#### Funds

ObsPy was partially funded by the

- German Science Foundation (DFG) via grant DFG IG 16/9-1
- German Ministry for Education and Research (BMBF), GEOTECHNOLOGIEN grant 03G0646H.
- NERA project (Network of European Research Infrastructures for Earthquake Risk Assessment and Mitigation) under the European Community's Seventh Framework Programme (FP7/2007-2013) grant agreement n° 262330
- Leibniz Institute for Applied Geophysics (LIAG)
- VERCE EU-FP7 project (no. 283543)