diff --git a/docs/advanced/classes.rst b/docs/advanced/classes.rst index 492790206..3dfc0c935 100644 --- a/docs/advanced/classes.rst +++ b/docs/advanced/classes.rst @@ -259,7 +259,7 @@ override the ``name()`` method): .. note:: - Note the trailing commas in the ``PYBIND11_OVERIDE`` calls to ``name()`` + Note the trailing commas in the ``PYBIND11_OVERRIDE`` calls to ``name()`` and ``bark()``. These are needed to portably implement a trampoline for a function that does not take any arguments. For functions that take a nonzero number of arguments, the trailing comma must be omitted. diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 7ffadcc51..a8145720e 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -525,8 +525,8 @@ protected: auto self_value_and_holder = value_and_holder(); if (overloads->is_constructor) { - if (!PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) { - PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid `self` argument"); + if (!parent || !PyObject_TypeCheck(parent.ptr(), (PyTypeObject *) overloads->scope.ptr())) { + PyErr_SetString(PyExc_TypeError, "__init__(self, ...) called with invalid or missing `self` argument"); return nullptr; } diff --git a/pybind11/setup_helpers.py b/pybind11/setup_helpers.py index c69064ca5..84a4584c5 100644 --- a/pybind11/setup_helpers.py +++ b/pybind11/setup_helpers.py @@ -47,6 +47,7 @@ import tempfile import threading import platform import warnings +import sysconfig try: from setuptools.command.build_ext import build_ext as _build_ext @@ -59,7 +60,7 @@ import distutils.errors import distutils.ccompiler -WIN = sys.platform.startswith("win32") +WIN = sys.platform.startswith("win32") and sysconfig.get_platform() != "mingw" PY2 = sys.version_info[0] < 3 MACOS = sys.platform.startswith("darwin") STD_TMPL = "/std:c++{}" if WIN else "-std=c++{}" diff --git a/tests/env.py b/tests/env.py index 5cded4412..6172b451b 100644 --- a/tests/env.py +++ b/tests/env.py @@ -2,6 +2,8 @@ import platform import sys +import pytest + LINUX = sys.platform.startswith("linux") MACOS = sys.platform.startswith("darwin") WIN = sys.platform.startswith("win32") or sys.platform.startswith("cygwin") @@ -12,3 +14,20 @@ PYPY = platform.python_implementation() == "PyPy" PY2 = sys.version_info.major == 2 PY = sys.version_info + + +def deprecated_call(): + """ + pytest.deprecated_call() seems broken in pytest<3.9.x; concretely, it + doesn't work on CPython 3.8.0 with pytest==3.3.2 on Ubuntu 18.04 (#2922). + + This is a narrowed reimplementation of the following PR :( + https://github.com/pytest-dev/pytest/pull/4104 + """ + # TODO: Remove this when testing requires pytest>=3.9. + pieces = pytest.__version__.split(".") + pytest_major_minor = (int(pieces[0]), int(pieces[1])) + if pytest_major_minor < (3, 9): + return pytest.warns((DeprecationWarning, PendingDeprecationWarning)) + else: + return pytest.deprecated_call() diff --git a/tests/test_builtin_casters.py b/tests/test_builtin_casters.py index fda038433..5ee8603a6 100644 --- a/tests/test_builtin_casters.py +++ b/tests/test_builtin_casters.py @@ -301,7 +301,7 @@ def test_int_convert(): cant_convert(3.14159) # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) if (3, 8) <= env.PY < (3, 10): - with pytest.deprecated_call(): + with env.deprecated_call(): assert convert(Int()) == 42 else: assert convert(Int()) == 42 @@ -336,7 +336,7 @@ def test_numpy_int_convert(): # The implicit conversion from np.float32 is undesirable but currently accepted. # TODO: Avoid DeprecationWarning in `PyLong_AsLong` (and similar) if (3, 8) <= env.PY < (3, 10): - with pytest.deprecated_call(): + with env.deprecated_call(): assert convert(np.float32(3.14159)) == 3 else: assert convert(np.float32(3.14159)) == 3 diff --git a/tests/test_factory_constructors.py b/tests/test_factory_constructors.py index ffcce6fd4..8c6ca173a 100644 --- a/tests/test_factory_constructors.py +++ b/tests/test_factory_constructors.py @@ -486,7 +486,9 @@ def test_invalid_self(): # Same as above, but for a class with an alias: class BrokenTF6(m.TestFactory6): def __init__(self, bad): - if bad == 1: + if bad == 0: + m.TestFactory6.__init__() + elif bad == 1: a = m.TestFactory2(tag.pointer, 1) m.TestFactory6.__init__(a, tag.base, 1) elif bad == 2: @@ -506,13 +508,13 @@ def test_invalid_self(): BrokenTF1(arg) assert ( str(excinfo.value) - == "__init__(self, ...) called with invalid `self` argument" + == "__init__(self, ...) called with invalid or missing `self` argument" ) - for arg in (1, 2, 3, 4): + for arg in (0, 1, 2, 3, 4): with pytest.raises(TypeError) as excinfo: BrokenTF6(arg) assert ( str(excinfo.value) - == "__init__(self, ...) called with invalid `self` argument" + == "__init__(self, ...) called with invalid or missing `self` argument" )