Meaning: percentage of code for which a test exists, determined by number of line executed during tests
pytest-cov
Instructions:
pytest-cov
using pip
/conda
pytest -vv --cov=./
pytest -vv --cov=./ --cov-report html
# content of test_sample.py
def inc(x):
if x < 0:
return x - 1
return x + 1
def test_answer():
assert inc(3) == 4
$ pytest -vv test_sample.py --cov=./
main.py::test_answer PASSED
---------- coverage: platform darwin, python 3.5.4-final-0 -----------
Name Stmts Miss Cover
-----------------------------
main.py 6 1 83%
# content of test_sample.py
def inc(x):
if x < 0:
return x - 1
return x + 1
def test_answer():
assert inc(3) == 4
def test_answer_negative():
assert inc(-2) == -3
$ pytest -vv test_sample.py --cov=./
main.py::test_answer PASSED
main.py::test_answer_negative PASSED
---------- coverage: platform darwin, python 3.5.4-final-0 -----------
Name Stmts Miss Cover
-----------------------------
main.py 8 0 100%
Meaning: ensure all changes to your project pass tests through automated test & build process
.travis.yml
file to the repository
language: python
), what it needs to build your
package (install
), and how to run the tests (language: script
)
language: python
python:
- "3.5"
- "3.6"
install:
- pip install -r requirements.txt
script:
- pytest -vv --cov=./;
requirements.txt
#
####### requirements.txt #######
#
###### Requirements without Version Specifiers ######
pytest
pytest-cov
#
###### Requirements with Version Specifiers ######
# See https://www.python.org/dev/peps/pep-0440/#version-specifiers
numpy >= 1.12.0
pint >= 0.7.2
#
language: python
python:
- "3.5"
- "3.6"
install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh;
fi
- bash miniconda.sh -b -p $HOME/miniconda
- rm miniconda.sh
- source $HOME/miniconda/etc/profile.d/conda.sh && conda activate
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda update -q --all
- conda config --append channels conda-forge
- conda env create -qf test-environment.yaml;
- source activate test-environment;
script:
- pytest -vv --cov=./;
test-environment.yaml
name: test-environment
channels:
- defaults
- conda-forge
dependencies:
- python=${PYTHON}
- numpy>=1.12.0
- pytest>=3.2.0
- pytest-cov
- pint>=0.7.2
git add .travis.yml test-environment.yaml
, commit, and push to GitHub
GitHub will use Travis CI to tell you if Pull Requests are safe to merge!
Professor Carole Goble in “Better Software, Better Research”:
One of my favorite #overlyhonestmethods tweets (a hashtag for lab scientists) is Ian Holmes’s “You can download our code from the URL supplied. Good luck downloading the only postdoc who can get it to run, though.”
README
: sits in top-level directory and
contains all the necessary information for installing, getting started with, and understanding the accompanying code.
May be accompanied by other specific files:
LICENSE
, INSTALL
, CITATION
, ABOUT
, CHANGELOG
SQUIRREL, version 1.2 released on 2026-09-20
# About
The Spectral Q and U Imaging Radiation Replicating Experimental Library
(SQUIRREL) is a library for replicating radiation sources with spectral details
and Q and U polarizations of superman bubblegum.
# Installation
The SQUIRREL library relies on other libraries:
- The ACORN library www.acorn.nutz
- The TREEBRANCH database format API
Install those before installing the SQUIRREL library. To install the SQUIRREL
library:
./configure
make --prefix=/install/path
make install
...
Comments provide a way to insert metainformation about code intended for people, right next to the code:
def the_function(var):
"""This is a docstring, where a function definition might live"""
a = 1 + var # this is a simple comment
return a
Also possible to pollute code with unnecessary cruft:
def decay(index, database):
# first, retrieve the decay constants from the database
mylist = database.decay_constants()
# next, try to access an element of the list
try:
d = mylist[index] # gets decay constant at index in the list
# if the index doesn't exist
except IndexError:
# throw an informative error message
raise Exception("value not found in the list")
return d
Code written cleanly will have its own voice. Use intelligent naming to make most lines of code clear without comments, then use comments sparingly to help explain reasons or complicated sections:
def decay(index, database):
lambdas = database.decay_constants()
try:
lambda_i = lambdas[index] # gets decay constant at index in the list
except IndexError:
raise Exception("value not found in the list")
return lambda
Naming: a class, variable, or function name should tell you why it exists, what it does, and how it is used.
Simple functions: functions should be small to be understandable and testable; they should only do one thing.
Consistent style: use a consistent, standardized style; e.g., select variable and function names according to the PEP8 style guide for Python.
# packages and modules are short and lowercase
packages
modules
# other objects can be long
ClassesUseCamelCase
ExceptionsAreClassesToo
functions_use_snake_case
CONSTANTS_USE_ALL_CAPS
# variable scope is *suggested* by style convention
_single_leading_underscore_ # internal to module
single_trailing_underscore_ # avoids conflicts with Python keywords
__double_leading_and_trailing__ # these are magic, like __init__
docstring: comment placed immediately after a function or class definition, typically enclosed by three pairs of double quotes:
def <name>(<args>):
"""<docstring>"""
<body>
docstrings are available within Python via help()
and
iPython's magic command ?
, and Sphinx picks them up.
Make docstrings descriptive and concise; you can explain the arguments of a function, its behavior, and how you intend it to be used.
def power(base, x):
"""Computes base^x. Both base and x should be integers,
floats, or another numeric type.
"""
return base**x
Sphinx can be used to automate the generation of HTML documentation; we can even use it with Travis CI to automatically build and deploy the docs on GitHub Pages.
For now, let's just make sure your docstrings are suitable for Sphinx.
def function_with_types_in_docstring(param1, param2):
"""Example function with types documented in the docstring.
`PEP 484`_ type annotations are supported. If attribute, parameter, and
return types are annotated according to `PEP 484`_, they do not need to be
included in the docstring:
Parameters
----------
param1 : int
The first parameter.
param2 : str
The second parameter.
Returns
-------
bool
True if successful, False otherwise.
.. _PEP 484:
https://www.python.org/dev/peps/pep-0484/
"""
More examples at the Sphinx documentation
def function_with_types_in_docstring(param1, param2):
"""Example function with types documented in the docstring.
`PEP 484`_ type annotations are supported. If attribute, parameter, and
return types are annotated according to `PEP 484`_, they do not need to be
included in the docstring:
Args:
param1 (int): The first parameter.
param2 (str): The second parameter.
Returns:
bool: The return value. True for success, False otherwise.
.. _PEP 484:
https://www.python.org/dev/peps/pep-0484/
"""
More examples at the Sphinx documentation
mkdir docs
cd docs
sphinx-quickstart
(accept defaults if unsure; answer "yes" for question about
autodoc
)
source
directory holds .rst
files for user guides, theory manuals, etc.
separate from the autogenerated API documentation