Skip to content

Commit

Permalink
Merge c934af3 into bba7bba
Browse files Browse the repository at this point in the history
  • Loading branch information
felddy committed May 6, 2019
2 parents bba7bba + c934af3 commit 1ff2bad
Show file tree
Hide file tree
Showing 14 changed files with 184 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .bandit.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
# Configuration file for the Bandit python security scanner
# https://bandit.readthedocs.io/en/latest/config.html
# This config is applied to bandit when scanning the "tests" tree

# Tests are first included by `tests`, and then excluded by `skips`.
# If `tests` is empty, all tests are are considered included.
Expand All @@ -10,4 +11,4 @@ tests:
# - B102

skips:
# - B101 # skip "assert used" check since assertions are required in pytests
- B101 # skip "assert used" check since assertions are required in pytests
12 changes: 12 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This is the configuration for code coverage checks
# https://coverage.readthedocs.io/en/latest/config.html

[run]
source = src/example
omit =
branch = true

[report]
exclude_lines =
if __name__ == "__main__":
show_missing = true
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
*.egg-info
__pycache__
.python-version
.coverage
.pytest_cache
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,22 @@ repos:
rev: v1.16.3
hooks:
- id: pyupgrade
# Run bandit on "tests" tree with a configuration
- repo: https://github.com/PyCQA/bandit
rev: 2a1dbab
hooks:
- id: bandit
name: bandit (tests tree)
files: tests
args:
- --config=.bandit.yml
# Run bandit everything but tests directory
- repo: https://github.com/PyCQA/bandit
rev: 2a1dbab
hooks:
- id: bandit
name: bandit (everything else)
exclude: tests
- repo: https://github.com/python/black
rev: 19.3b0
hooks:
Expand Down
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ install:
- pip install --upgrade -r requirements-test.txt
script:
- pre-commit run --all-files
- pytest
after_success:
- coveralls
19 changes: 15 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ one.
If you choose to [submit a pull
request](https://github.com/cisagov/skeleton-python-library/pulls),
you will notice that our continuous integration (CI) system runs a
fairly extensive set of linters and syntax checkers. Your pull
request may fail these checks, and that's OK. If you want you can
stop there and wait for us to make the necessary corrections to ensure
your code passes the CI checks.
fairly extensive set of linters, syntax checkers, system, and unit tests.
Your pull request may fail these checks, and that's OK. If you want
you can stop there and wait for us to make the necessary corrections
to ensure your code passes the CI checks.

If you want to make the changes yourself, or if you want to become a
regular contributor, then you will want to set up
Expand Down Expand Up @@ -96,6 +96,17 @@ At this point the pre-commit checks will run against any files that
you attempt to commit. If you want to run the checks against the
entire repo, just execute `pre-commit run --all-files`.

### Running unit and system tests ###

In addition to the pre-commit checks the CI system will run the suite
of unit and system tests that are included with this project. To run
these tests locally execute `pytest` from the root of the project.

We encourage any updates to these tests to improve the overall code
coverage. If your pull request adds new functionality we would
appreciate it if you extend existing test cases, or add new ones to
exercise the newly added code.

## Public domain ##

This project is in the public domain within the United States, and
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# skeleton-python-library #

[![Build Status](https://travis-ci.com/cisagov/skeleton-python-library.svg?branch=develop)](https://travis-ci.com/cisagov/skeleton-python-library)
[![Coverage Status](https://coveralls.io/repos/github/cisagov/skeleton-python-library/badge.svg?branch=develop)](https://coveralls.io/github/cisagov/skeleton-python-library?branch=develop)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/cisagov/skeleton-python-library.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/cisagov/skeleton-python-library/alerts/)
[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/cisagov/skeleton-python-library.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/cisagov/skeleton-python-library/context:python)

Expand Down
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
addopts = -v -ra --cov
25 changes: 20 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
- https://packaging.python.org/distributing/
- https://github.com/pypa/sampleproject/blob/master/setup.py
- https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure
"""

from setuptools import setup
from glob import glob
from os.path import splitext, basename

from setuptools import setup, find_packages


def readme():
Expand All @@ -16,10 +20,18 @@ def readme():
return f.read()


def package_vars(version_file):
"""Read in and return the variables defined by the version_file."""
pkg_vars = {}
with open(version_file) as f:
exec(f.read(), pkg_vars) # nosec
return pkg_vars


setup(
name="add",
name="example",
# Versions should comply with PEP440
version="0.0.1",
version=package_vars("src/example/_version.py")["__version__"],
description="Example python library",
long_description=readme(),
long_description_content_type="text/markdown",
Expand Down Expand Up @@ -50,9 +62,12 @@ def readme():
],
# What does your project relate to?
keywords="skeleton",
packages=["example"],
packages=find_packages(where="src"),
package_dir={"": "src"},
py_modules=[splitext(basename(path))[0] for path in glob("src/*.py")],
include_package_data=True,
install_requires=["docopt"],
extras_require={"test": ["pre-commit"]},
extras_require={"test": ["pre-commit", "pytest", "pytest-cov", "coveralls"]},
# Conveniently allows one to run the CLI tool as `example`
entry_points={"console_scripts": ["example = example.example:main"]},
)
5 changes: 5 additions & 0 deletions src/example/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""The example library."""
from .example import example_div
from ._version import __version__ # noqa: F401

__all__ = ["example_div"]
2 changes: 2 additions & 0 deletions src/example/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
"""This file defines the version of this module."""
__version__ = "0.0.1"
11 changes: 7 additions & 4 deletions example/example.py → src/example/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,22 @@

import docopt

from ._version import __version__

def example():

def example_div(x, y):
"""Print some logging messages."""
logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")
logging.critical("This is a critical message")
return x / y


def main():
"""Set up logging and call the example function."""
args = docopt.docopt(__doc__, version="0.0.1")

args = docopt.docopt(__doc__, version=__version__)
# Set up logging
log_level = args["--log-level"]
try:
Expand All @@ -45,10 +47,11 @@ def main():
)
return 1

example()
print(example_div(8, 2))

# Stop logging and clean up
logging.shutdown()
return 0


if __name__ == "__main__":
Expand Down
23 changes: 23 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""pytest plugin configuration.
https://docs.pytest.org/en/latest/writing_plugins.html#conftest-py-plugins
"""
import pytest


def pytest_addoption(parser):
"""Add new commandline options to pytest."""
parser.addoption(
"--runslow", action="store_true", default=False, help="run slow tests"
)


def pytest_collection_modifyitems(config, items):
"""Modify collected tests based on custom marks and commandline options."""
if config.getoption("--runslow"):
# --runslow given in cli: do not skip slow tests
return
skip_slow = pytest.mark.skip(reason="need --runslow option to run")
for item in items:
if "slow" in item.keywords:
item.add_marker(skip_slow)
80 changes: 80 additions & 0 deletions tests/test_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#!/usr/bin/env pytest -vs
"""Tests for example."""

import logging
import sys
from unittest.mock import patch

import pytest

import example

div_params = [
(1, 1, 1),
(2, 2, 1),
(0, 1, 0),
(8, 2, 4),
pytest.param(0, 0, 0, marks=pytest.mark.xfail(raises=ZeroDivisionError)),
]

log_levels = (
"debug",
"info",
"warning",
"error",
"critical",
pytest.param("critical2", marks=pytest.mark.xfail),
)


def test_version(capsys):
"""Verify that version string sent to stdout, and agrees with the module."""
with pytest.raises(SystemExit):
with patch.object(sys, "argv", ["bogus", "--version"]):
example.example.main()
captured = capsys.readouterr()
assert (
captured.out == f"{example.__version__}\n"
), "standard output by '--version' should agree with module.__version__"


@pytest.mark.parametrize("level", log_levels)
def test_log_levels(level):
"""Validate commandline log-level arguments."""
with patch.object(sys, "argv", ["bogus", f"--log-level={level}"]):
with patch.object(logging.root, "handlers", []):
assert (
logging.root.hasHandlers() is False
), "root logger should not have handlers yet"
return_code = example.example.main()
assert (
logging.root.hasHandlers() is True
), "root logger should now have a handler"
assert return_code == 0, "main() should return success (0)"


@pytest.mark.parametrize("dividend, divisor, quotient", div_params)
def test_division(dividend, divisor, quotient):
"""Verify division results."""
result = example.example_div(dividend, divisor)
assert result == quotient, "result should equal quotient"


@pytest.mark.slow
def test_slow_division():
"""Example of using a custom marker.
This test will only be run if --runslow is passed to pytest.
Look in conftest.py to see how this is implemented.
"""
import time

result = example.example_div(256, 16)
time.sleep(4)
assert result == 16, "result should equal be 16"


def test_zero_division():
"""Verify that division by zero throws the correct exception."""
with pytest.raises(ZeroDivisionError):
example.example_div(1, 0)

0 comments on commit 1ff2bad

Please sign in to comment.