diff --git a/.appveyor.yml b/.appveyor.yml index 3d49d56bb0..8fbb726108 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -42,14 +42,14 @@ install: if ($env:PYTHON) { if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" - python -m pip install --upgrade pip wheel - python -m pip install pytest numpy --no-warn-script-location + python -W ignore -m pip install --upgrade pip wheel + python -W ignore -m pip install pytest numpy --no-warn-script-location } elseif ($env:CONDA) { if ($env:CONDA -eq "27") { $env:CONDA = "" } if ($env:PLATFORM -eq "x64") { $env:CONDA = "$env:CONDA-x64" } $env:PATH = "C:\Miniconda$env:CONDA\;C:\Miniconda$env:CONDA\Scripts\;$env:PATH" $env:PYTHONHOME = "C:\Miniconda$env:CONDA" - conda update -y -n base conda + conda --version conda install -y -q pytest numpy scipy } - ps: | @@ -62,6 +62,7 @@ build_script: -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DCMAKE_SUPPRESS_REGENERATION=1 + . - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - cmake --build . --config %CONFIG% --target pytest -- /m /v:m /logger:%MSBuildLogger% - cmake --build . --config %CONFIG% --target cpptest -- /m /v:m /logger:%MSBuildLogger% diff --git a/.travis.yml b/.travis.yml index d7f300e6b4..28542a8763 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ language: cpp -dist: trusty -sudo: false matrix: include: # This config does a few things: @@ -11,20 +9,21 @@ matrix: # also tests the automatic discovery functions in CMake (Python version, C++ standard). - os: linux dist: xenial # Necessary to run doxygen 1.8.15 - env: STYLE DOCS PIP + name: Style, docs, and pip cache: false before_install: - pyenv global $(pyenv whence 2to3) # activate all python versions - PY_CMD=python3 - $PY_CMD -m pip install --user --upgrade pip wheel setuptools - install: - - $PY_CMD -m pip install --user --upgrade sphinx sphinx_rtd_theme breathe flake8 pep8-naming pytest + install: # Breathe does not yet support Sphinx 2 + - $PY_CMD -m pip install --user --upgrade "sphinx<2" sphinx_rtd_theme breathe flake8 pep8-naming pytest - curl -fsSL https://sourceforge.net/projects/doxygen/files/rel-1.8.15/doxygen-1.8.15.linux.bin.tar.gz/download | tar xz - export PATH="$PWD/doxygen-1.8.15/bin:$PATH" script: - tools/check-style.sh - flake8 - - $PY_CMD -m sphinx -W -b html docs docs/.build + # TODO(eric.cousineau): Re-enable doc build at some point once `breathe` is unbroken. + # - $PY_CMD -m sphinx -W -b html docs docs/.build - | # Make sure setup.py distributes and installs all the headers $PY_CMD setup.py sdist @@ -33,62 +32,119 @@ matrix: diff -rq $installed ./include/pybind11 - | # Barebones build - cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) + cmake -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DPYTHON_EXECUTABLE=$(which $PY_CMD) . make pytest -j 2 make cpptest -j 2 # The following are regular test configurations, including optional dependencies. # With regard to each other they differ in Python version, C++ standard and compiler. - os: linux + dist: trusty + name: Python 2.7, c++11, gcc 4.8 env: PYTHON=2.7 CPP=11 GCC=4.8 addons: apt: - packages: [cmake=2.\*, cmake-data=2.\*] + packages: + - cmake=2.\* + - cmake-data=2.\* - os: linux + dist: trusty + name: Python 3.6, c++11, gcc 4.8 env: PYTHON=3.6 CPP=11 GCC=4.8 addons: apt: - sources: [deadsnakes] - packages: [python3.6-dev python3.6-venv, cmake=2.\*, cmake-data=2.\*] - - sudo: true - services: docker + sources: + - deadsnakes + packages: + - python3.6-dev + - python3.6-venv + - cmake=2.\* + - cmake-data=2.\* + - os: linux + dist: trusty env: PYTHON=2.7 CPP=14 GCC=6 CMAKE=1 - - sudo: true - services: docker - env: PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 - - sudo: true + name: Python 2.7, c++14, gcc 4.8, CMake test + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + - os: linux + dist: trusty + name: Python 3.5, c++14, gcc 6, Debug build + # N.B. `ensurepip` could be installed transitively by `python3.5-venv`, but + # seems to have apt conflicts (at least for Trusty). Use Docker instead. services: docker + env: DOCKER=debian:stretch PYTHON=3.5 CPP=14 GCC=6 DEBUG=1 + - os: linux + dist: xenial env: PYTHON=3.6 CPP=17 GCC=7 + name: Python 3.6, c++17, gcc 7 + addons: + apt: + sources: + - deadsnakes + - ubuntu-toolchain-r-test + packages: + - g++-7 + - python3.6-dev + - python3.6-venv - os: linux - env: PYTHON=3.6 CPP=17 CLANG=5.0 + dist: xenial + env: PYTHON=3.6 CPP=17 CLANG=7 + name: Python 3.6, c++17, Clang 7 addons: apt: - sources: [deadsnakes, llvm-toolchain-trusty-5.0, ubuntu-toolchain-r-test] - packages: [python3.6-dev python3.6-venv clang-5.0 llvm-5.0-dev, lld-5.0] + sources: + - deadsnakes + - llvm-toolchain-xenial-7 + packages: + - python3.6-dev + - python3.6-venv + - clang-7 + - libclang-7-dev + - llvm-7-dev + - lld-7 + - libc++-7-dev + - libc++abi-7-dev # Why is this necessary??? - os: osx + name: Python 2.7, c++14, AppleClang 7.3, CMake test osx_image: xcode7.3 env: PYTHON=2.7 CPP=14 CLANG CMAKE=1 - os: osx + name: Python 3.7, c++14, AppleClang 9, Debug build osx_image: xcode9 env: PYTHON=3.7 CPP=14 CLANG DEBUG=1 # Test a PyPy 2.7 build - os: linux + dist: trusty env: PYPY=5.8 PYTHON=2.7 CPP=11 GCC=4.8 + name: PyPy 5.8, Python 2.7, c++11, gcc 4.8 addons: apt: - packages: [libblas-dev, liblapack-dev, gfortran] + packages: + - libblas-dev + - liblapack-dev + - gfortran # Build in 32-bit mode and tests against the CMake-installed version - - sudo: true + - os: linux + dist: trusty services: docker - env: ARCH=i386 PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 + env: DOCKER=i386/debian:stretch PYTHON=3.5 CPP=14 GCC=6 INSTALL=1 + name: Python 3.4, c++14, gcc 6, 32-bit script: - | - $SCRIPT_RUN_PREFIX sh -c "set -e - cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 - make install - cp -a tests /pybind11-tests - mkdir /build-tests && cd /build-tests - cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON - make pytest -j 2" + # Consolidated 32-bit Docker Build + Install + set -ex + $SCRIPT_RUN_PREFIX sh -c " + set -ex + cmake ${CMAKE_EXTRA_ARGS} -DPYBIND11_INSTALL=1 -DPYBIND11_TEST=0 . + make install + cp -a tests /pybind11-tests + mkdir /build-tests && cd /build-tests + cmake ../pybind11-tests ${CMAKE_EXTRA_ARGS} -DPYBIND11_WERROR=ON + make pytest -j 2" + set +ex cache: directories: - $HOME/.local/bin @@ -98,6 +154,7 @@ cache: before_install: - | # Configure build variables + set -ex if [ "$TRAVIS_OS_NAME" = "linux" ]; then if [ -n "$CLANG" ]; then export CXX=clang++-$CLANG CC=clang-$CLANG @@ -108,18 +165,16 @@ before_install: fi export CXX=g++-$GCC CC=gcc-$GCC fi - if [ "$GCC" = "6" ]; then DOCKER=${ARCH:+$ARCH/}debian:stretch - elif [ "$GCC" = "7" ]; then DOCKER=debian:buster EXTRA_PACKAGES+=" catch python3-distutils" DOWNLOAD_CATCH=OFF - fi elif [ "$TRAVIS_OS_NAME" = "osx" ]; then export CXX=clang++ CC=clang; fi if [ -n "$CPP" ]; then CPP=-std=c++$CPP; fi if [ "${PYTHON:0:1}" = "3" ]; then PY=3; fi if [ -n "$DEBUG" ]; then CMAKE_EXTRA_ARGS+=" -DCMAKE_BUILD_TYPE=Debug"; fi + set +ex - | # Initialize environment - set -e + set -ex if [ -n "$DOCKER" ]; then docker pull $DOCKER @@ -148,13 +203,15 @@ before_install: if [ "$PY" = 3 ] || [ -n "$PYPY" ]; then $PY_CMD -m ensurepip --user fi + $PY_CMD --version $PY_CMD -m pip install --user --upgrade pip wheel fi - set +e + set +ex install: - | # Install dependencies - set -e + set -ex + cmake --version if [ -n "$DOCKER" ]; then if [ -n "$DEBUG" ]; then PY_DEBUG="python$PYTHON-dbg python$PY-scipy-dbg" @@ -166,32 +223,21 @@ install: libeigen3-dev libboost-dev cmake make ${EXTRA_PACKAGES} && break; done" else - if [ "$CLANG" = "5.0" ]; then - if ! [ -d ~/.local/include/c++/v1 ]; then - # Neither debian nor llvm provide a libc++ 5.0 deb; luckily it's fairly quick - # to build, install (and cache), so do it ourselves: - git clone --depth=1 https://github.com/llvm-mirror/llvm.git llvm-source - git clone https://github.com/llvm-mirror/libcxx.git llvm-source/projects/libcxx -b release_50 - git clone https://github.com/llvm-mirror/libcxxabi.git llvm-source/projects/libcxxabi -b release_50 - mkdir llvm-build && cd llvm-build - # Building llvm requires a newer cmake than is provided by the trusty container: - CMAKE_VER=cmake-3.8.0-Linux-x86_64 - curl https://cmake.org/files/v3.8/$CMAKE_VER.tar.gz | tar xz - ./$CMAKE_VER/bin/cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/.local ../llvm-source - make -j2 install-cxxabi install-cxx - cp -a include/c++/v1/*cxxabi*.h ~/.local/include/c++/v1 - cd .. - fi - export CXXFLAGS="-isystem $HOME/.local/include/c++/v1 -stdlib=libc++" - export LDFLAGS="-L$HOME/.local/lib -fuse-ld=lld-$CLANG" - export LD_LIBRARY_PATH="$HOME/.local/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" - if [ "$CPP" = "-std=c++17" ]; then CPP="-std=c++1z"; fi + if [ "$CLANG" = "7" ]; then + export CXXFLAGS="-stdlib=libc++" fi export NPY_NUM_BUILD_JOBS=2 echo "Installing pytest, numpy, scipy..." - ${PYPY:+travis_wait 30} $PY_CMD -m pip install --user --upgrade pytest numpy scipy \ - ${PYPY:+--extra-index-url https://imaginary.ca/trusty-pypi} + local PIP_CMD="" + if [ -n $PYPY ]; then + # For expediency, install only versions that are available on the extra index. + travis_wait 30 \ + $PY_CMD -m pip install --user --upgrade --extra-index-url https://imaginary.ca/trusty-pypi \ + pytest numpy==1.15.4 scipy==1.2.0 + else + $PY_CMD -m pip install --user --upgrade pytest numpy scipy + fi echo "done." mkdir eigen @@ -199,16 +245,37 @@ install: tar --extract -j --directory=eigen --strip-components=1 export CMAKE_INCLUDE_PATH="${CMAKE_INCLUDE_PATH:+$CMAKE_INCLUDE_PATH:}$PWD/eigen" fi - set +e + set +ex script: -- $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS} - -DPYBIND11_PYTHON_VERSION=$PYTHON - -DPYBIND11_CPP_STANDARD=$CPP - -DPYBIND11_WERROR=${WERROR:-ON} - -DDOWNLOAD_CATCH=${DOWNLOAD_CATCH:-ON} -- $SCRIPT_RUN_PREFIX make pytest -j 2 -- $SCRIPT_RUN_PREFIX make cpptest -j 2 -- if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi +- | + # CMake Configuration + set -ex + $SCRIPT_RUN_PREFIX cmake ${CMAKE_EXTRA_ARGS} \ + -DPYBIND11_PYTHON_VERSION=$PYTHON \ + -DPYBIND11_CPP_STANDARD=$CPP \ + -DPYBIND11_WERROR=${WERROR:-ON} \ + -DDOWNLOAD_CATCH=${DOWNLOAD_CATCH:-ON} \ + . + set +ex +- | + # pytest + set -ex + $SCRIPT_RUN_PREFIX make pytest -j 2 VERBOSE=1 + set +ex +- | + # cpptest + set -ex + $SCRIPT_RUN_PREFIX make cpptest -j 2 + set +ex +- | + # CMake Build Interface + set -ex + if [ -n "$CMAKE" ]; then $SCRIPT_RUN_PREFIX make test_cmake_build; fi + set +ex after_failure: cat tests/test_cmake_build/*.log* after_script: -- if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi +- | + # Cleanup (Docker) + set -ex + if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi + set +ex diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index d9e7677115..e8e0b2dc39 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -855,14 +855,14 @@ template class array_t : public // Reference to element at a given index template const T& at(Ix... index) const { - if (sizeof...(index) != ndim()) + if ((ssize_t) sizeof...(index) != ndim()) fail_dim_check(sizeof...(index), "index dimension mismatch"); return *(static_cast(array::data()) + byte_offset(ssize_t(index)...) / itemsize()); } // Mutable reference to element at a given index template T& mutable_at(Ix... index) { - if (sizeof...(index) != ndim()) + if ((ssize_t) sizeof...(index) != ndim()) fail_dim_check(sizeof...(index), "index dimension mismatch"); return *(static_cast(array::mutable_data()) + byte_offset(ssize_t(index)...) / itemsize()); } @@ -1527,7 +1527,7 @@ struct vectorize_helper { if (trivial == broadcast_trivial::f_trivial) result = array_t(shape); else result = array_t(shape); - if (size == 0) return result; + if (size == 0) return std::move(result); /* Call the function */ if (trivial == broadcast_trivial::non_trivial) @@ -1535,7 +1535,7 @@ struct vectorize_helper { else apply_trivial(buffers, params, result.mutable_data(), size, i_seq, vi_seq, bi_seq); - return result; + return std::move(result); } template diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 55b16ad0ad..2878e5b2dd 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -2380,12 +2380,12 @@ class gil_scoped_release { }; #endif error_already_set::~error_already_set() { - if (type) { + if (m_type) { error_scope scope; gil_scoped_acquire gil; - type.release().dec_ref(); - value.release().dec_ref(); - trace.release().dec_ref(); + m_type.release().dec_ref(); + m_value.release().dec_ref(); + m_trace.release().dec_ref(); } } diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index ea2d39c667..406339f80d 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -325,7 +325,7 @@ class error_already_set : public std::runtime_error { /// Constructs a new exception from the current Python error indicator, if any. The current /// Python error indicator will be cleared. error_already_set() : std::runtime_error(detail::error_string()) { - PyErr_Fetch(&type.ptr(), &value.ptr(), &trace.ptr()); + PyErr_Fetch(&m_type.ptr(), &m_value.ptr(), &m_trace.ptr()); } error_already_set(const error_already_set &) = default; @@ -336,7 +336,7 @@ class error_already_set : public std::runtime_error { /// Give the currently-held error back to Python, if any. If there is currently a Python error /// already set it is cleared first. After this call, the current object no longer stores the /// error variables (but the `.what()` string is still available). - void restore() { PyErr_Restore(type.release().ptr(), value.release().ptr(), trace.release().ptr()); } + void restore() { PyErr_Restore(m_type.release().ptr(), m_value.release().ptr(), m_trace.release().ptr()); } // Does nothing; provided for backwards compatibility. PYBIND11_DEPRECATED("Use of error_already_set.clear() is deprecated") @@ -345,10 +345,14 @@ class error_already_set : public std::runtime_error { /// Check if the currently trapped error type matches the given Python exception class (or a /// subclass thereof). May also be passed a tuple to search for any exception class matches in /// the given tuple. - bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), type.ptr()); } + bool matches(handle ex) const { return PyErr_GivenExceptionMatches(ex.ptr(), m_type.ptr()); } + + const object& type() const { return m_type; } + const object& value() const { return m_value; } + const object& trace() const { return m_trace; } private: - object type, value, trace; + object m_type, m_value, m_trace; }; /** \defgroup python_builtins _ @@ -389,6 +393,14 @@ inline bool hasattr(handle obj, const char *name) { return PyObject_HasAttrString(obj.ptr(), name) == 1; } +inline void delattr(handle obj, handle name) { + if (PyObject_DelAttr(obj.ptr(), name.ptr()) != 0) { throw error_already_set(); } +} + +inline void delattr(handle obj, const char *name) { + if (PyObject_DelAttrString(obj.ptr(), name) != 0) { throw error_already_set(); } +} + inline object getattr(handle obj, handle name) { PyObject *result = PyObject_GetAttr(obj.ptr(), name.ptr()); if (!result) { throw error_already_set(); } @@ -460,7 +472,6 @@ object object_or_cast(T &&o); // Match a PyObject*, which we want to convert directly to handle via its converting constructor inline handle object_or_cast(PyObject *ptr) { return ptr; } - template class accessor : public object_api> { using key_type = typename Policy::key_type; diff --git a/setup.cfg b/setup.cfg index 9bbbd03f68..002f38d10e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,4 +7,6 @@ show_source = True exclude = .git, __pycache__, build, dist, docs, tools, venv ignore = # required for pretty matrix formatting: multiple spaces after `,` and `[` - E201, E241, W504 + E201, E241, W504, + # camelcase 'cPickle' imported as lowercase 'pickle' + N813 diff --git a/tests/conftest.py b/tests/conftest.py index 0b76395cc8..55d9d0d536 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -75,7 +75,7 @@ def __enter__(self): self.capfd.readouterr() return self - def __exit__(self, *_): + def __exit__(self, *args): self.out, self.err = self.capfd.readouterr() def __eq__(self, other): diff --git a/tests/test_gil_scoped.cpp b/tests/test_gil_scoped.cpp index a94b7a2e27..cb0010ee6b 100644 --- a/tests/test_gil_scoped.cpp +++ b/tests/test_gil_scoped.cpp @@ -13,6 +13,7 @@ class VirtClass { public: + virtual ~VirtClass() {} virtual void virtual_func() {} virtual void pure_virtual_func() = 0; }; diff --git a/tests/test_kwargs_and_defaults.cpp b/tests/test_kwargs_and_defaults.cpp index 2263b6b7a4..6563fb9ad3 100644 --- a/tests/test_kwargs_and_defaults.cpp +++ b/tests/test_kwargs_and_defaults.cpp @@ -34,7 +34,9 @@ TEST_SUBMODULE(kwargs_and_defaults, m) { m.def("kw_func_udl_z", kw_func, "x"_a, "y"_a=0); // test_args_and_kwargs - m.def("args_function", [](py::args args) -> py::tuple { return args; }); + m.def("args_function", [](py::args args) -> py::tuple { + return std::move(args); + }); m.def("args_kwargs_function", [](py::args args, py::kwargs kwargs) { return py::make_tuple(args, kwargs); }); diff --git a/tests/test_operator_overloading.cpp b/tests/test_operator_overloading.cpp index 4ad34d104c..8ca7d8bcff 100644 --- a/tests/test_operator_overloading.cpp +++ b/tests/test_operator_overloading.cpp @@ -62,6 +62,25 @@ namespace std { }; } +// MSVC warns about unknown pragmas, and warnings are errors. +#ifndef _MSC_VER + #pragma GCC diagnostic push + // clang 7.0.0 and Apple LLVM 10.0.1 introduce `-Wself-assign-overloaded` to + // `-Wall`, which is used here for overloading (e.g. `py::self += py::self `). + // Here, we suppress the warning using `#pragma diagnostic`. + // Taken from: https://github.com/RobotLocomotion/drake/commit/aaf84b46 + // TODO(eric): This could be resolved using a function / functor (e.g. `py::self()`). + #if (__APPLE__) && (__clang__) + #if (__clang_major__ >= 10) && (__clang_minor__ >= 0) && (__clang_patchlevel__ >= 1) + #pragma GCC diagnostic ignored "-Wself-assign-overloaded" + #endif + #elif (__clang__) + #if (__clang_major__ >= 7) + #pragma GCC diagnostic ignored "-Wself-assign-overloaded" + #endif + #endif +#endif + TEST_SUBMODULE(operators, m) { // test_operator_overloading @@ -144,3 +163,7 @@ TEST_SUBMODULE(operators, m) { .def_readwrite("b", &NestC::b); m.def("get_NestC", [](const NestC &c) { return c.value; }); } + +#ifndef _MSC_VER + #pragma GCC diagnostic pop +#endif diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp index e82941ef22..f7c46339c0 100644 --- a/tests/test_smart_ptr.cpp +++ b/tests/test_smart_ptr.cpp @@ -337,7 +337,9 @@ TEST_SUBMODULE(smart_ptr, m) { // test_shared_ptr_gc // #187: issue involving std::shared_ptr<> return value policy & garbage collection - struct ElementBase { virtual void foo() { } /* Force creation of virtual table */ }; + struct ElementBase { + virtual ~ElementBase() { } /* Force creation of virtual table */ + }; py::class_>(m, "ElementBase"); struct ElementA : ElementBase { diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp index c9a561c09f..ccf018d997 100644 --- a/tests/test_virtual_functions.cpp +++ b/tests/test_virtual_functions.cpp @@ -129,6 +129,7 @@ class Movable { class NCVirt { public: + virtual ~NCVirt() { } virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); } virtual Movable get_movable(int a, int b) = 0;