Skip to content

Commit

Permalink
Add checks to maintain a consistent Python code style and prevent bugs (
Browse files Browse the repository at this point in the history
pybind#515)

A flake8 configuration is included in setup.cfg and the checks are
executed automatically on Travis:

* Ensures a consistent PEP8 code style
* Does basic linting to prevent possible bugs
  • Loading branch information
dean0x7d authored and wjakob committed Nov 20, 2016
1 parent df81546 commit bad1740
Show file tree
Hide file tree
Showing 22 changed files with 163 additions and 170 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ matrix:
# Documentation build:
- os: linux
language: docs
env: DOCS STYLE
install: pip install sphinx sphinx_rtd_theme
env: DOCS STYLE LINT
install: pip install sphinx sphinx_rtd_theme flake8 pep8-naming
script:
- make -C docs html SPHINX_OPTIONS=-W
- tools/check-style.sh
- flake8
cache:
directories:
- $HOME/.cache/pip
Expand Down
2 changes: 1 addition & 1 deletion pybind11/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ._version import version_info, __version__
from ._version import version_info, __version__ # noqa: F401 imported but unused


def get_include(*args, **kwargs):
Expand Down
9 changes: 9 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
[bdist_wheel]
universal=1

[flake8]
show_source = True
exclude = .git, __pycache__, build, dist, docs, tools, venv
ignore =
# line too long
E501,
# required for pretty matrix formating: multiple spaces after `,` and `[`
E201, E241
7 changes: 3 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
import textwrap
import difflib
import re
import os
import sys
import contextlib

_unicode_marker = re.compile(r'u(\'[^\']*\')')
_long_marker = re.compile(r'([0-9])L')
_hexadecimal = re.compile(r'0x[0-9a-fA-F]+')
_long_marker = re.compile(r'([0-9])L')
_hexadecimal = re.compile(r'0x[0-9a-fA-F]+')


def _strip_and_dedent(s):
Expand Down Expand Up @@ -218,7 +217,7 @@ def _test_import_pybind11():
"""
# noinspection PyBroadException
try:
import pybind11_tests
import pybind11_tests # noqa: F401 imported but unused
except Exception as e:
print("Failed to import pybind11_tests from pytest:")
print(" {}: {}".format(type(e).__name__, e))
Expand Down
25 changes: 12 additions & 13 deletions tests/test_alias_initialization.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import pytest
import gc

def test_alias_delay_initialization(capture, msg):

# A only initializes its trampoline class when we inherit from it; if we
# just create and use an A instance directly, the trampoline initialization
# is bypassed and we only initialize an A() instead (for performance
# reasons)
def test_alias_delay_initialization1(capture):
"""A only initializes its trampoline class when we inherit from it; if we just
create and use an A instance directly, the trampoline initialization is bypassed
and we only initialize an A() instead (for performance reasons).
"""
from pybind11_tests import A, call_f

class B(A):
Expand Down Expand Up @@ -37,14 +36,14 @@ def f(self):
PyA.~PyA()
"""

def test_alias_delay_initialization(capture, msg):
from pybind11_tests import A2, call_f

# A2, unlike the above, is configured to always initialize the alias; while
# the extra initialization and extra class layer has small virtual dispatch
# performance penalty, it also allows us to do more things with the
# trampoline class such as defining local variables and performing
# construction/destruction.
def test_alias_delay_initialization2(capture):
"""A2, unlike the above, is configured to always initialize the alias; while
the extra initialization and extra class layer has small virtual dispatch
performance penalty, it also allows us to do more things with the trampoline
class such as defining local variables and performing construction/destruction.
"""
from pybind11_tests import A2, call_f

class B2(A2):
def __init__(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_chrono.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_chrono_duration_roundtrip():
from pybind11_tests import test_chrono3
import datetime

# Get the difference betwen two times (a timedelta)
# Get the difference between two times (a timedelta)
date1 = datetime.datetime.today()
date2 = datetime.datetime.today()
diff = date2 - date1
Expand Down
1 change: 0 additions & 1 deletion tests/test_class_args.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@

import pytest

def test_class_args():
# There's basically nothing to test here; just make sure the code compiled and declared its definition
Expand Down
2 changes: 0 additions & 2 deletions tests/test_copy_move_policies.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,3 @@ def test_lacking_move_ctor():
with pytest.raises(RuntimeError) as excinfo:
lacking_move_ctor.get_one()
assert "the object is neither movable nor copyable!" in str(excinfo.value)


4 changes: 2 additions & 2 deletions tests/test_docstring_options.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pybind11_tests import ConstructorStats

def test_docstring_options(capture):

def test_docstring_options():
from pybind11_tests import (test_function1, test_function2, test_function3,
test_function4, test_function5, test_function6,
test_function7, DocstringTestFoo)
Expand Down
7 changes: 2 additions & 5 deletions tests/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ def test_unscoped_enum():
assert not (UnscopedEnum.ETwo < UnscopedEnum.EOne)
assert not (2 < UnscopedEnum.EOne)

def test_scoped_enum():
from pybind11_tests import ScopedEnum, test_scoped_enum

assert test_scoped_enum(ScopedEnum.Three) == "ScopedEnum::Three"

def test_scoped_enum():
from pybind11_tests import ScopedEnum, test_scoped_enum
Expand All @@ -56,6 +52,7 @@ def test_scoped_enum():
assert ScopedEnum.Two >= ScopedEnum.Two
assert ScopedEnum.Three >= ScopedEnum.Two


def test_implicit_conversion():
from pybind11_tests import ClassWithUnscopedEnum

Expand Down Expand Up @@ -87,6 +84,7 @@ def test_implicit_conversion():
# Hashing test
assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}"


def test_binary_operators():
from pybind11_tests import Flags

Expand All @@ -108,4 +106,3 @@ def test_binary_operators():
state2 = ~state
assert state2 == -7
assert int(state ^ state2) == -1

2 changes: 1 addition & 1 deletion tests/test_eval_call.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file is called from 'test_eval.py'

if 'call_test2' in locals():
call_test2(y)
call_test2(y) # noqa: F821 undefined name
2 changes: 1 addition & 1 deletion tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,6 @@ def test_custom(msg):
with pytest.raises(MyException5) as excinfo:
try:
throws5()
except MyException5_1 as e:
except MyException5_1:
raise RuntimeError("Exception error: caught child from parent")
assert msg(excinfo.value) == "this is a helper-defined translated exception"
33 changes: 17 additions & 16 deletions tests/test_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ def test_no_id(msg):
assert expect_float(12) == 12



def test_str_issue(msg):
"""Issue #283: __str__ called on uninitialized instance when constructor arguments invalid"""
from pybind11_tests.issues import StrIssue
Expand Down Expand Up @@ -153,8 +152,8 @@ def test_override_ref():
o = OverrideTest("asdf")

# Not allowed (see associated .cpp comment)
#i = o.str_ref()
#assert o.str_ref() == "asdf"
# i = o.str_ref()
# assert o.str_ref() == "asdf"
assert o.str_value() == "asdf"

assert o.A_value().value == "hi"
Expand All @@ -167,15 +166,17 @@ def test_override_ref():
def test_operators_notimplemented(capture):
from pybind11_tests.issues import OpTest1, OpTest2
with capture:
C1, C2 = OpTest1(), OpTest2()
C1 + C1
C2 + C2
C2 + C1
C1 + C2
assert capture == """Add OpTest1 with OpTest1
Add OpTest2 with OpTest2
Add OpTest2 with OpTest1
Add OpTest2 with OpTest1"""
c1, c2 = OpTest1(), OpTest2()
c1 + c1
c2 + c2
c2 + c1
c1 + c2
assert capture == """
Add OpTest1 with OpTest1
Add OpTest2 with OpTest2
Add OpTest2 with OpTest1
Add OpTest2 with OpTest1
"""


def test_iterator_rvpolicy():
Expand All @@ -185,7 +186,7 @@ def test_iterator_rvpolicy():

assert list(make_iterator_1()) == [1, 2, 3]
assert list(make_iterator_2()) == [1, 2, 3]
assert(type(make_iterator_1()) != type(make_iterator_2()))
assert not isinstance(make_iterator_1(), type(make_iterator_2()))


def test_dupe_assignment():
Expand Down Expand Up @@ -234,9 +235,9 @@ def test_complex_cast(capture):
test_complex(2j)

assert capture == """
1.0
(0.0, 2.0)
"""
1.0
(0.0, 2.0)
"""


def test_inheritance_override_def_static():
Expand Down
1 change: 0 additions & 1 deletion tests/test_keep_alive.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,3 @@ def test_return_none(capture):
del p
gc.collect()
assert capture == "Releasing parent."

3 changes: 2 additions & 1 deletion tests/test_methods_and_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ def test_property_return_value_policies(access):

def test_property_rvalue_policy():
"""When returning an rvalue, the return value policy is automatically changed from
`reference(_internal)` to `move`. The following would not work otherwise."""
`reference(_internal)` to `move`. The following would not work otherwise.
"""
from pybind11_tests import TestPropRVP

instance = TestPropRVP()
Expand Down
9 changes: 4 additions & 5 deletions tests/test_multiple_inheritance.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pytest


def test_multiple_inheritance_cpp(msg):
def test_multiple_inheritance_cpp():
from pybind11_tests import MIType

mt = MIType(3, 4)
Expand All @@ -10,7 +9,7 @@ def test_multiple_inheritance_cpp(msg):
assert mt.bar() == 4


def test_multiple_inheritance_mix1(msg):
def test_multiple_inheritance_mix1():
from pybind11_tests import Base2

class Base1:
Expand All @@ -31,7 +30,7 @@ def __init__(self, i, j):
assert mt.bar() == 4


def test_multiple_inheritance_mix2(msg):
def test_multiple_inheritance_mix2():
from pybind11_tests import Base1

class Base2:
Expand All @@ -52,7 +51,7 @@ def __init__(self, i, j):
assert mt.bar() == 4


def test_multiple_inheritance_virtbase(msg):
def test_multiple_inheritance_virtbase():
from pybind11_tests import Base12a, bar_base2a, bar_base2a_sharedptr

class MITypePy(Base12a):
Expand Down
91 changes: 45 additions & 46 deletions tests/test_numpy_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,10 @@ def test_bounds_check(arr):
mutate_data, mutate_data_t, at_t, mutate_at_t)
for func in funcs:
with pytest.raises(IndexError) as excinfo:
index_at(arr, 2, 0)
func(arr, 2, 0)
assert str(excinfo.value) == 'index 2 is out of bounds for axis 0 with size 2'
with pytest.raises(IndexError) as excinfo:
index_at(arr, 0, 4)
func(arr, 0, 4)
assert str(excinfo.value) == 'index 4 is out of bounds for axis 1 with size 3'


Expand All @@ -166,50 +166,49 @@ def test_make_c_f_array():
def test_wrap():
from pybind11_tests.array import wrap

def assert_references(A, B):
assert A is not B
assert A.__array_interface__['data'][0] == \
B.__array_interface__['data'][0]
assert A.shape == B.shape
assert A.strides == B.strides
assert A.flags.c_contiguous == B.flags.c_contiguous
assert A.flags.f_contiguous == B.flags.f_contiguous
assert A.flags.writeable == B.flags.writeable
assert A.flags.aligned == B.flags.aligned
assert A.flags.updateifcopy == B.flags.updateifcopy
assert np.all(A == B)
assert not B.flags.owndata
assert B.base is A
if A.flags.writeable and A.ndim == 2:
A[0, 0] = 1234
assert B[0, 0] == 1234

A1 = np.array([1, 2], dtype=np.int16)
assert A1.flags.owndata and A1.base is None
A2 = wrap(A1)
assert_references(A1, A2)

A1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='F')
assert A1.flags.owndata and A1.base is None
A2 = wrap(A1)
assert_references(A1, A2)

A1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='C')
A1.flags.writeable = False
A2 = wrap(A1)
assert_references(A1, A2)

A1 = np.random.random((4, 4, 4))
A2 = wrap(A1)
assert_references(A1, A2)

A1 = A1.transpose()
A2 = wrap(A1)
assert_references(A1, A2)

A1 = A1.diagonal()
A2 = wrap(A1)
assert_references(A1, A2)
def assert_references(a, b):
assert a is not b
assert a.__array_interface__['data'][0] == b.__array_interface__['data'][0]
assert a.shape == b.shape
assert a.strides == b.strides
assert a.flags.c_contiguous == b.flags.c_contiguous
assert a.flags.f_contiguous == b.flags.f_contiguous
assert a.flags.writeable == b.flags.writeable
assert a.flags.aligned == b.flags.aligned
assert a.flags.updateifcopy == b.flags.updateifcopy
assert np.all(a == b)
assert not b.flags.owndata
assert b.base is a
if a.flags.writeable and a.ndim == 2:
a[0, 0] = 1234
assert b[0, 0] == 1234

a1 = np.array([1, 2], dtype=np.int16)
assert a1.flags.owndata and a1.base is None
a2 = wrap(a1)
assert_references(a1, a2)

a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='F')
assert a1.flags.owndata and a1.base is None
a2 = wrap(a1)
assert_references(a1, a2)

a1 = np.array([[1, 2], [3, 4]], dtype=np.float32, order='C')
a1.flags.writeable = False
a2 = wrap(a1)
assert_references(a1, a2)

a1 = np.random.random((4, 4, 4))
a2 = wrap(a1)
assert_references(a1, a2)

a1 = a1.transpose()
a2 = wrap(a1)
assert_references(a1, a2)

a1 = a1.diagonal()
a2 = wrap(a1)
assert_references(a1, a2)


@pytest.requires_numpy
Expand Down
Loading

0 comments on commit bad1740

Please sign in to comment.