Skip to content

Merge 'upstream/master' (114be7f4) #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.cmake @henryiii
CMakeLists.txt @henryiii
*.yml @henryiii
*.yaml @henryiii
/tools/ @henryiii
/pybind11/ @henryiii
2 changes: 1 addition & 1 deletion docs/advanced/classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,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.
Expand Down
2 changes: 0 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
breathe==4.26.1
commonmark==0.9.1
recommonmark==0.7.1
sphinx==3.3.1
sphinx_rtd_theme==0.5.0
sphinxcontrib-moderncmakedomain==3.17
Expand Down
5 changes: 5 additions & 0 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,11 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
template <typename Derived>
template <return_value_policy policy, typename... Args>
object object_api<Derived>::operator()(Args &&...args) const {
#if !defined(NDEBUG) && PY_VERSION_HEX >= 0x03060000
if (!PyGILState_Check()) {
pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure.");
}
#endif
return detail::collect_arguments<policy>(std::forward<Args>(args)...).call(derived().ptr());
}

Expand Down
2 changes: 1 addition & 1 deletion include/pybind11/chrono.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ template <typename type> class duration_caster {
using rep = typename type::rep;
using period = typename type::period;

using days = std::chrono::duration<uint_fast32_t, std::ratio<86400>>;
using days = std::chrono::duration<int_least32_t, std::ratio<86400>>; // signed 25 bits required by the standard.

bool load(handle src, bool) {
using namespace std::chrono;
Expand Down
16 changes: 11 additions & 5 deletions include/pybind11/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,17 @@ struct type_caster<std::function<Return(Args...)>> {
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(cfunc.ptr()));
auto rec = (function_record *) c;

if (rec && rec->is_stateless &&
same_type(typeid(function_type), *reinterpret_cast<const std::type_info *>(rec->data[1]))) {
struct capture { function_type f; };
value = ((capture *) &rec->data)->f;
return true;
while (rec != nullptr) {
if (rec->is_stateless
&& same_type(typeid(function_type),
*reinterpret_cast<const std::type_info *>(rec->data[1]))) {
struct capture {
function_type f;
};
value = ((capture *) &rec->data)->f;
return true;
}
rec = rec->next;
}
}

Expand Down
10 changes: 5 additions & 5 deletions include/pybind11/pybind11.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,12 @@ class cpp_function : public function {
/* Without these pragmas, GCC warns that there might not be
enough space to use the placement new operator. However, the
'if' statement above ensures that this is the case. */
#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wplacement-new"
#endif
new ((capture *) &rec->data) capture { std::forward<Func>(f) };
#if defined(__GNUG__) && !defined(__clang__) && __GNUC__ >= 6
#if defined(__GNUG__) && __GNUC__ >= 6 && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
if (!std::is_trivially_destructible<Func>::value)
Expand Down Expand Up @@ -558,8 +558,8 @@ class cpp_function : public function {

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;
}

Expand Down Expand Up @@ -2693,6 +2693,6 @@ PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
# pragma warning(pop)
#elif defined(__GNUG__) && !defined(__clang__)
#elif defined(__GNUG__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
3 changes: 2 additions & 1 deletion pybind11/setup_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import threading
import platform
import warnings
import sysconfig

try:
from setuptools.command.build_ext import build_ext as _build_ext
Expand All @@ -59,7 +60,7 @@
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++{}"
Expand Down
9 changes: 8 additions & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,15 @@ function(pybind11_enable_warnings target_name)
target_compile_options(${target_name} PRIVATE /WX)
elseif(PYBIND11_CUDA_TESTS)
target_compile_options(${target_name} PRIVATE "SHELL:-Werror all-warnings")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|IntelLLVM)")
target_compile_options(${target_name} PRIVATE -Werror)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
target_compile_options(
${target_name}
PRIVATE
-Werror-all
# "Inlining inhibited by limit max-size", "Inlining inhibited by limit max-total-size"
-diag-disable 11074,11076)
endif()
endif()

Expand Down
19 changes: 19 additions & 0 deletions tests/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -12,3 +14,20 @@
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()
4 changes: 2 additions & 2 deletions tests/test_builtin_casters.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ def cant_convert(v):
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
Expand Down Expand Up @@ -336,7 +336,7 @@ def require_implicit(v):
# 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
Expand Down
8 changes: 8 additions & 0 deletions tests/test_callbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ TEST_SUBMODULE(callbacks, m) {
// test_cpp_function_roundtrip
/* Test if passing a function pointer from C++ -> Python -> C++ yields the original pointer */
m.def("dummy_function", &dummy_function);
m.def("dummy_function_overloaded", [](int i, int j) { return i + j; });
m.def("dummy_function_overloaded", &dummy_function);
m.def("dummy_function2", [](int i, int j) { return i + j; });
m.def("roundtrip", [](std::function<int(int)> f, bool expect_none = false) {
if (expect_none && f)
Expand Down Expand Up @@ -172,4 +174,10 @@ TEST_SUBMODULE(callbacks, m) {
for (auto i : work)
start_f(py::cast<int>(i));
});

m.def("callback_num_times", [](py::function f, std::size_t num) {
for (std::size_t i = 0; i < num; i++) {
f();
}
});
}
36 changes: 36 additions & 0 deletions tests/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import pytest
from pybind11_tests import callbacks as m
from threading import Thread
import time


def test_callbacks():
Expand Down Expand Up @@ -92,6 +93,10 @@ def test_cpp_function_roundtrip():
m.test_dummy_function(m.roundtrip(m.dummy_function))
== "matches dummy_function: eval(1) = 2"
)
assert (
m.test_dummy_function(m.dummy_function_overloaded)
== "matches dummy_function: eval(1) = 2"
)
assert m.roundtrip(None, expect_none=True) is None
assert (
m.test_dummy_function(lambda x: x + 2)
Expand Down Expand Up @@ -146,3 +151,34 @@ def test_async_async_callbacks():
t = Thread(target=test_async_callbacks)
t.start()
t.join()


def test_callback_num_times():
# Super-simple micro-benchmarking related to PR #2919.
# Example runtimes (Intel Xeon 2.2GHz, fully optimized):
# num_millions 1, repeats 2: 0.1 secs
# num_millions 20, repeats 10: 11.5 secs
one_million = 1000000
num_millions = 1 # Try 20 for actual micro-benchmarking.
repeats = 2 # Try 10.
rates = []
for rep in range(repeats):
t0 = time.time()
m.callback_num_times(lambda: None, num_millions * one_million)
td = time.time() - t0
rate = num_millions / td if td else 0
rates.append(rate)
if not rep:
print()
print(
"callback_num_times: {:d} million / {:.3f} seconds = {:.3f} million / second".format(
num_millions, td, rate
)
)
if len(rates) > 1:
print("Min Mean Max")
print(
"{:6.3f} {:6.3f} {:6.3f}".format(
min(rates), sum(rates) / len(rates), max(rates)
)
)
35 changes: 13 additions & 22 deletions tests/test_chrono.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ def test_chrono_system_clock_roundtrip():

# They should be identical (no information lost on roundtrip)
diff = abs(date1 - date2)
assert diff.days == 0
assert diff.seconds == 0
assert diff.microseconds == 0
assert diff == datetime.timedelta(0)


def test_chrono_system_clock_roundtrip_date():
Expand All @@ -64,9 +62,7 @@ def test_chrono_system_clock_roundtrip_date():
assert diff.microseconds == 0

# Year, Month & Day should be the same after the round trip
assert date1.year == date2.year
assert date1.month == date2.month
assert date1.day == date2.day
assert date1 == date2

# There should be no time information
assert time2.hour == 0
Expand Down Expand Up @@ -117,10 +113,7 @@ def test_chrono_system_clock_roundtrip_time(time1, tz, monkeypatch):
assert isinstance(time2, datetime.time)

# Hour, Minute, Second & Microsecond should be the same after the round trip
assert time1.hour == time2.hour
assert time1.minute == time2.minute
assert time1.second == time2.second
assert time1.microsecond == time2.microsecond
assert time1 == time2

# There should be no date information (i.e. date = python base date)
assert date2.year == 1970
Expand All @@ -140,9 +133,13 @@ def test_chrono_duration_roundtrip():

cpp_diff = m.test_chrono3(diff)

assert cpp_diff.days == diff.days
assert cpp_diff.seconds == diff.seconds
assert cpp_diff.microseconds == diff.microseconds
assert cpp_diff == diff

# Negative timedelta roundtrip
diff = datetime.timedelta(microseconds=-1)
cpp_diff = m.test_chrono3(diff)

assert cpp_diff == diff


def test_chrono_duration_subtraction_equivalence():
Expand All @@ -153,9 +150,7 @@ def test_chrono_duration_subtraction_equivalence():
diff = date2 - date1
cpp_diff = m.test_chrono4(date2, date1)

assert cpp_diff.days == diff.days
assert cpp_diff.seconds == diff.seconds
assert cpp_diff.microseconds == diff.microseconds
assert cpp_diff == diff


def test_chrono_duration_subtraction_equivalence_date():
Expand All @@ -166,9 +161,7 @@ def test_chrono_duration_subtraction_equivalence_date():
diff = date2 - date1
cpp_diff = m.test_chrono4(date2, date1)

assert cpp_diff.days == diff.days
assert cpp_diff.seconds == diff.seconds
assert cpp_diff.microseconds == diff.microseconds
assert cpp_diff == diff


def test_chrono_steady_clock():
Expand All @@ -183,9 +176,7 @@ def test_chrono_steady_clock_roundtrip():
assert isinstance(time2, datetime.timedelta)

# They should be identical (no information lost on roundtrip)
assert time1.days == time2.days
assert time1.seconds == time2.seconds
assert time1.microseconds == time2.microseconds
assert time1 == time2


def test_floating_point_duration():
Expand Down
8 changes: 4 additions & 4 deletions tests/test_constants_and_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ int f1(int x) noexcept { return x+1; }
#endif
int f2(int x) noexcept(true) { return x+2; }
int f3(int x) noexcept(false) { return x+3; }
#if defined(__GNUG__)
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated"
#endif
int f4(int x) throw() { return x+4; } // Deprecated equivalent to noexcept(true)
#if defined(__GNUG__)
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
struct C {
Expand All @@ -71,13 +71,13 @@ struct C {
int m4(int x) const noexcept(true) { return x-4; }
int m5(int x) noexcept(false) { return x-5; }
int m6(int x) const noexcept(false) { return x-6; }
#if defined(__GNUG__)
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated"
#endif
int m7(int x) throw() { return x-7; }
int m8(int x) const throw() { return x-8; }
#if defined(__GNUG__)
#if defined(__GNUG__) && !defined(__INTEL_COMPILER)
# pragma GCC diagnostic pop
#endif
};
Expand Down
10 changes: 6 additions & 4 deletions tests/test_factory_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,9 @@ def __init__(self, bad):
# 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:
Expand All @@ -506,13 +508,13 @@ def __init__(self, bad):
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"
)
Loading