MDTraj Programming Style

PEP8

PEP8 is the python style guide. Follow it. Having consistent style makes the code easy to read and easy to use. This is especially important for public methods (i.e. they should use underlines_to_separate_words and not camelCase), but its also important for internal variable names, so that the code is easy to read and not jarring.

There are a few static code checkers that can help you with PEP8 compliance. I like flake8. There’s also pylint.

pip install flake8 pylint

There are a few stupid flake8 errors that you can overlook, but most of them should be corrected. The ones that are probably alright to skip are E501 (line too ling), E231, and a few others. Sometimes you just need more than 80 characters. Readability is what counts. Use your judgement. But err on the side of following PEP8. To ignore specific warnings with flake8, you can run it as so.

flake8 --ignore=errors=E501,E231 MDTraj/

Properties

This is not java. Don’t write get_myattribute and set_myattribute methods. Use the @property decorator. See the astropy coding guidelines for details.

Writing Tests

Untested code is broken code. So testing isn’t really a feature of programming style, it’s about correctness. Even tested code might be broken code, but untested code is by definition broken, and that’s worse. Writing tests is annoying, but it pays off big time. Nose makes writing tests pretty easy, and MDTraj has some testing infrastructure to make it easier.

Tests should go in a file named test_modulename.py, either in MDTraj/test/ or in the test directory in a subpackage, like MDTraj/geometry/test. If a subpackage only contains one test module, then it’s fine to put the test module in the package directory directly, without making a test subdirectory. This is how the mdtraj.pdb subpackage’s tests are organized currently.

MDTraj contains some infrastructure to help with testing. The most useful is the function mdtraj.testing.eq,

mdtraj.testing.eq(o1, o2, decimal=6, err_msg='')

Convenience function for asserting that two objects are equal to one another

If the objects are both arrays or sparse matrices, this method will dispatch to an appropriate handler, which makes it a little bit more useful than just calling assert o1 == o2 (which wont work for numpy arrays – it returns an array of bools, not a single True or False)

Parameters:

o1 : object

The first object

o2 : object

The second object

decimal : int

If the two objects are floats or arrays of floats, they’ll be checked for equality up to this decimal place.

err_msg : str

Custom error message

Returns:

passed : bool

True if the tests pass. If the tests doesn’t pass, since the AssertionError will be raised

Raises:

AssertionError

If the tests fail

mdtraj.testing.get_fn(name)

Get the full path to one of the reference files shipped for testing

In the source distribution, these files are in MDTraj/testing/reference, but on installation, they’re moved to somewhere in the user’s python site-packages directory.

Parameters:

name : str

Name of the file to load (with respect to the reference/ folder).

Examples

>>> import mdtraj as md
>>> t = md.load(get_fn('2EQQ.pdb'))
>>> eq(t.n_frames, 20)    # this runs the assert, using the eq() func.

Docstrings

Every function should have a docstring. This is critical. Most of the information in the documentation is pulled directly from the docstrings, which means that the docstrings are really important. In order to have sphinx properly parse the docstrings, they need to be written in the numpy format. A detailed description of the numpy docstring format is available here.

Cython Docstrings

in cython code, the function signature cannot be introspected directly, due to limitations of python. We get around that by writing the signature manually on the first line of the docstring. For instance, the docstring for XTCTrajectoryFormat.read() is below. Note that the signature comes before the 1 line summary.

"""read(n_frames=None, stride=1, atom_indices=None)

Read data from an XTC file

Parameters
----------
n_frames : int, None
    The number of frames you would like to read from the file.
    If None, all of the remaining frames will be loaded.
stride : int, optional
    Read only every stride-th frame.
atom_indices : array_like, optional
    If not none, then read only a subset of the atoms coordinates from the
    file. This may be slightly slower than the standard read because it required
    an extra copy, but will save memory.

Returns
-------
xyz : np.ndarray, shape=(n_frames, n_atoms, 3), dtype=np.float32
    The cartesian coordinates, in nanometers
time : np.ndarray, shape=(n_frames), dtype=np.float32
    The simulation time, in picoseconds, corresponding to each frame
step : np.ndarray, shape=(n_frames), dtype=np.int32
    The step in the simulation corresponding to each frame
box : np.ndarray, shape=(n_frames, 3, 3), dtype=np.float32
    The box vectors in each frame.
"""

Class Docstrings

Make sure to include the constructor parameters in the class docstring, not the __init__ docstring. This is more consistent with how the constructor is actually invoked, since users don’t call cls.__init__(...), they call cls(...).

Also, it’s a good idea to put an attributes section in the class docstring, even if the attributes are @property decorated functions.

Versions