Skip to content

Commit 08d5ce0

Browse files
committed
Add flake8 and pylint to unittests
1 parent 61bc2ab commit 08d5ce0

4 files changed

Lines changed: 210 additions & 0 deletions

File tree

tests/__init__.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright (C) 2017, Benjamin Drung <benjamin.drung@profitbricks.com>
2+
#
3+
# Permission to use, copy, modify, and/or distribute this software for any
4+
# purpose with or without fee is hereby granted, provided that the above
5+
# copyright notice and this permission notice appear in all copies.
6+
#
7+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
15+
import inspect
16+
import os
17+
import sys
18+
import unittest
19+
20+
21+
def get_source_files():
22+
"""Return a list of sources files/directories (to check with flake8/pylint)"""
23+
scripts = []
24+
modules = ["examples", "profitbricks", "tests"]
25+
py_files = ["setup.py"]
26+
27+
files = []
28+
for code_file in scripts + modules + py_files:
29+
is_script = code_file in scripts
30+
if not os.path.exists(code_file): # pragma: no cover
31+
# The alternative path is needed for Debian's pybuild
32+
alternative = os.path.join(os.environ.get("OLDPWD", ""), code_file)
33+
code_file = alternative if os.path.exists(alternative) else code_file
34+
if is_script:
35+
with open(code_file, "rb") as script_file:
36+
shebang = script_file.readline().decode("utf-8")
37+
if ((sys.version_info[0] == 3 and "python3" in shebang)
38+
or ("python" in shebang and "python3" not in shebang)):
39+
files.append(code_file)
40+
else:
41+
files.append(code_file)
42+
return files
43+
44+
45+
def unittest_verbosity():
46+
"""Return the verbosity setting of the currently running unittest
47+
program, or None if none is running.
48+
"""
49+
frame = inspect.currentframe()
50+
while frame:
51+
self = frame.f_locals.get("self")
52+
if isinstance(self, unittest.TestProgram):
53+
return self.verbosity
54+
frame = frame.f_back
55+
return None # pragma: no cover

tests/pylint.conf

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[MASTER]
2+
3+
# Pickle collected data for later comparisons.
4+
persistent=no
5+
6+
7+
[MESSAGES CONTROL]
8+
9+
# Disable the message, report, category or checker with the given id(s). You
10+
# can either give multiple identifiers separated by comma (,) or put this
11+
# option multiple times (only on the command line, not in the configuration
12+
# file where it should appear only once).You can also use "--disable=all" to
13+
# disable everything first and then reenable specific checks. For example, if
14+
# you want to run only the similarities checker, you can use "--disable=all
15+
# --enable=similarities". If you want to run only the classes checker, but have
16+
# no Warning level messages displayed, use"--disable=all --enable=classes
17+
# --disable=W"
18+
disable=fixme,invalid-name,locally-disabled,missing-docstring,too-few-public-methods,too-many-arguments,too-many-branches,too-many-instance-attributes,too-many-locals,too-many-lines,too-many-nested-blocks,too-many-public-methods,too-many-statements
19+
20+
21+
[REPORTS]
22+
23+
# Tells whether to display a full report or only the messages
24+
reports=no
25+
26+
27+
[FORMAT]
28+
29+
# Maximum number of characters on a single line.
30+
max-line-length=99
31+
32+
33+
[SIMILARITIES]
34+
35+
# Minimum lines number of a similarity.
36+
min-similarity-lines=119

tests/test_flake8.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright (C) 2017-2018, Benjamin Drung <bdrung@debian.org>
2+
#
3+
# Permission to use, copy, modify, and/or distribute this software for any
4+
# purpose with or without fee is hereby granted, provided that the above
5+
# copyright notice and this permission notice appear in all copies.
6+
#
7+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14+
15+
"""test_flake8.py - Run flake8 check"""
16+
17+
import subprocess
18+
import sys
19+
import unittest
20+
21+
from . import get_source_files, unittest_verbosity
22+
23+
24+
class Flake8TestCase(unittest.TestCase):
25+
"""
26+
This unittest class provides a test that runs the flake8 code
27+
checker (which combines pycodestyle and pyflakes) on the Python
28+
source code. The list of source files is provided by the
29+
get_source_files() function.
30+
"""
31+
32+
def test_flake8(self):
33+
"""Test: Run flake8 on Python source code"""
34+
cmd = [sys.executable, "-m", "flake8", "--max-line-length=99"] + get_source_files()
35+
if unittest_verbosity() >= 2:
36+
sys.stderr.write("Running following command:\n{}\n".format(" ".join(cmd)))
37+
process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
38+
stderr=subprocess.PIPE, close_fds=True)
39+
40+
out, err = process.communicate()
41+
if process.returncode != 0: # pragma: no cover
42+
msgs = []
43+
if err:
44+
msgs.append("flake8 exited with code {} and has unexpected output on stderr:\n{}"
45+
.format(process.returncode, err.decode().rstrip()))
46+
if out:
47+
msgs.append("flake8 found issues:\n{}".format(out.decode().rstrip()))
48+
if not msgs:
49+
msgs.append("flake8 exited with code {} and has no output on stdout or stderr."
50+
.format(process.returncode))
51+
self.fail("\n".join(msgs))

tests/test_pylint.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Copyright (C) 2010, Stefano Rivera <stefanor@debian.org>
2+
# Copyright (C) 2017-2018, Benjamin Drung <bdrung@debian.org>
3+
#
4+
# Permission to use, copy, modify, and/or distribute this software for any
5+
# purpose with or without fee is hereby granted, provided that the above
6+
# copyright notice and this permission notice appear in all copies.
7+
#
8+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9+
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10+
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11+
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12+
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13+
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14+
# PERFORMANCE OF THIS SOFTWARE.
15+
16+
"""test_pylint.py - Run pylint"""
17+
18+
import os
19+
import re
20+
import subprocess
21+
import sys
22+
import unittest
23+
24+
from . import get_source_files, unittest_verbosity
25+
26+
CONFIG = os.path.join(os.path.dirname(__file__), "pylint.conf")
27+
28+
29+
class PylintTestCase(unittest.TestCase):
30+
"""
31+
This unittest class provides a test that runs the pylint code check
32+
on the Python source code. The list of source files is provided by
33+
the get_source_files() function and pylint is purely configured via
34+
a config file.
35+
"""
36+
37+
def test_pylint(self):
38+
"""Test: Run pylint on Python source code"""
39+
40+
cmd = [sys.executable, "-m", "pylint", "--rcfile=" + CONFIG, "--"] + get_source_files()
41+
if unittest_verbosity() >= 2:
42+
sys.stderr.write("Running following command:\n{}\n".format(" ".join(cmd)))
43+
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
44+
close_fds=True)
45+
out, err = process.communicate()
46+
47+
if process.returncode != 0: # pragma: no cover
48+
# Strip trailing summary (introduced in pylint 1.7). This summary might look like:
49+
#
50+
# ------------------------------------
51+
# Your code has been rated at 10.00/10
52+
#
53+
out = re.sub("^(-+|Your code has been rated at .*)$", "", out.decode(),
54+
flags=re.MULTILINE).rstrip()
55+
56+
# Strip logging of used config file (introduced in pylint 1.8)
57+
err = re.sub("^Using config file .*\n", "", err.decode()).rstrip()
58+
59+
msgs = []
60+
if err:
61+
msgs.append("pylint exited with code {} and has unexpected output on stderr:\n{}"
62+
.format(process.returncode, err))
63+
if out:
64+
msgs.append("pylint found issues:\n{}".format(out))
65+
if not msgs:
66+
msgs.append("pylint exited with code {} and has no output on stdout or stderr."
67+
.format(process.returncode))
68+
self.fail("\n".join(msgs))

0 commit comments

Comments
 (0)