Skip to content

Commit ce62ec5

Browse files
committed
Merge branch 'master' into sh_merge_master
2 parents b4e1ac9 + 6c65ab5 commit ce62ec5

17 files changed

+254
-41
lines changed

.clang-tidy

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ modernize-use-override,
3131
modernize-use-using,
3232
*performance*,
3333
readability-avoid-const-params-in-decls,
34+
readability-const-return-type,
3435
readability-container-size-empty,
35-
readability-else-after-return,
3636
readability-delete-null-pointer,
37+
readability-else-after-return,
3738
readability-implicit-bool-conversion,
3839
readability-make-member-function-const,
3940
readability-misplaced-array-index,

include/pybind11/cast.h

+19-2
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,22 @@ template <typename StringType, bool IsView = false> struct string_caster {
399399
#endif
400400
}
401401

402+
#if PY_VERSION_HEX >= 0x03030000
403+
// On Python >= 3.3, for UTF-8 we avoid the need for a temporary `bytes`
404+
// object by using `PyUnicode_AsUTF8AndSize`.
405+
if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) {
406+
Py_ssize_t size = -1;
407+
const auto *buffer
408+
= reinterpret_cast<const CharT *>(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size));
409+
if (!buffer) {
410+
PyErr_Clear();
411+
return false;
412+
}
413+
value = StringType(buffer, static_cast<size_t>(size));
414+
return true;
415+
}
416+
#endif
417+
402418
auto utfNbytes = reinterpret_steal<object>(PyUnicode_AsEncodedString(
403419
load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr));
404420
if (!utfNbytes) { PyErr_Clear(); return false; }
@@ -1205,13 +1221,14 @@ class argument_loader {
12051221
}
12061222

12071223
template <typename Return, typename Guard, typename Func>
1224+
// NOLINTNEXTLINE(readability-const-return-type)
12081225
enable_if_t<!std::is_void<Return>::value, Return> call(Func &&f) && {
1209-
return std::move(*this).template call_impl<Return>(std::forward<Func>(f), indices{}, Guard{});
1226+
return std::move(*this).template call_impl<remove_cv_t<Return>>(std::forward<Func>(f), indices{}, Guard{});
12101227
}
12111228

12121229
template <typename Return, typename Guard, typename Func>
12131230
enable_if_t<std::is_void<Return>::value, void_type> call(Func &&f) && {
1214-
std::move(*this).template call_impl<Return>(std::forward<Func>(f), indices{}, Guard{});
1231+
std::move(*this).template call_impl<remove_cv_t<Return>>(std::forward<Func>(f), indices{}, Guard{});
12151232
return void_type();
12161233
}
12171234

include/pybind11/detail/common.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111

1212
#define PYBIND11_VERSION_MAJOR 2
1313
#define PYBIND11_VERSION_MINOR 8
14-
#define PYBIND11_VERSION_PATCH 0.dev1
14+
#define PYBIND11_VERSION_PATCH 0.dev2
1515

1616
// Similar to Python's convention: https://docs.python.org/3/c-api/apiabiversion.html
1717
// Additional convention: 0xD = dev
18-
#define PYBIND11_VERSION_HEX 0x020800D1
18+
#define PYBIND11_VERSION_HEX 0x020800D2
1919

2020
#define PYBIND11_NAMESPACE_BEGIN(name) namespace name {
2121
#define PYBIND11_NAMESPACE_END(name) }

include/pybind11/detail/descr.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ constexpr enable_if_t<B, T1> _(const T1 &d, const T2 &) { return d; }
7777
template <bool B, typename T1, typename T2>
7878
constexpr enable_if_t<!B, T2> _(const T1 &, const T2 &d) { return d; }
7979

80-
template <size_t Size> auto constexpr _() -> decltype(int_to_str<Size / 10, Size % 10>::digits) {
80+
template <size_t Size>
81+
auto constexpr _() -> remove_cv_t<decltype(int_to_str<Size / 10, Size % 10>::digits)> {
8182
return int_to_str<Size / 10, Size % 10>::digits;
8283
}
8384

include/pybind11/detail/internals.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ struct internals {
107107
std::unordered_map<const PyObject *, std::vector<PyObject *>> patients;
108108
std::forward_list<ExceptionTranslator> registered_exception_translators;
109109
std::unordered_map<std::string, void *> shared_data; // Custom data to be shared across extensions
110-
std::vector<PyObject *> loader_patient_stack; // Used by `loader_life_support`
110+
std::vector<PyObject *> unused_loader_patient_stack_remove_at_v5;
111111
std::forward_list<std::string> static_strings; // Stores the std::strings backing detail::c_str()
112112
PyTypeObject *static_property_type;
113113
PyTypeObject *default_metaclass;
@@ -305,12 +305,12 @@ PYBIND11_NOINLINE internals &get_internals() {
305305
#if PY_VERSION_HEX >= 0x03070000
306306
internals_ptr->tstate = PyThread_tss_alloc();
307307
if (!internals_ptr->tstate || (PyThread_tss_create(internals_ptr->tstate) != 0))
308-
pybind11_fail("get_internals: could not successfully initialize the TSS key!");
308+
pybind11_fail("get_internals: could not successfully initialize the tstate TSS key!");
309309
PyThread_tss_set(internals_ptr->tstate, tstate);
310310
#else
311311
internals_ptr->tstate = PyThread_create_key();
312312
if (internals_ptr->tstate == -1)
313-
pybind11_fail("get_internals: could not successfully initialize the TLS key!");
313+
pybind11_fail("get_internals: could not successfully initialize the tstate TLS key!");
314314
PyThread_set_key_value(internals_ptr->tstate, tstate);
315315
#endif
316316
internals_ptr->istate = tstate->interp;

include/pybind11/detail/type_caster_base.h

+31-24
Original file line numberDiff line numberDiff line change
@@ -31,47 +31,54 @@ PYBIND11_NAMESPACE_BEGIN(detail)
3131
/// A life support system for temporary objects created by `type_caster::load()`.
3232
/// Adding a patient will keep it alive up until the enclosing function returns.
3333
class loader_life_support {
34+
private:
35+
loader_life_support* parent = nullptr;
36+
std::unordered_set<PyObject *> keep_alive;
37+
38+
static loader_life_support** get_stack_pp() {
39+
#if defined(WITH_THREAD)
40+
thread_local static loader_life_support* per_thread_stack = nullptr;
41+
return &per_thread_stack;
42+
#else
43+
static loader_life_support* global_stack = nullptr;
44+
return &global_stack;
45+
#endif
46+
}
47+
3448
public:
3549
/// A new patient frame is created when a function is entered
3650
loader_life_support() {
37-
get_internals().loader_patient_stack.push_back(nullptr);
51+
loader_life_support** stack = get_stack_pp();
52+
parent = *stack;
53+
*stack = this;
3854
}
3955

4056
/// ... and destroyed after it returns
4157
~loader_life_support() {
42-
auto &stack = get_internals().loader_patient_stack;
43-
if (stack.empty())
58+
loader_life_support** stack = get_stack_pp();
59+
if (*stack != this)
4460
pybind11_fail("loader_life_support: internal error");
45-
46-
auto ptr = stack.back();
47-
stack.pop_back();
48-
Py_CLEAR(ptr);
49-
50-
// A heuristic to reduce the stack's capacity (e.g. after long recursive calls)
51-
if (stack.capacity() > 16 && !stack.empty() && stack.capacity() / stack.size() > 2)
52-
stack.shrink_to_fit();
61+
*stack = parent;
62+
for (auto* item : keep_alive)
63+
Py_DECREF(item);
5364
}
5465

5566
/// This can only be used inside a pybind11-bound function, either by `argument_loader`
5667
/// at argument preparation time or by `py::cast()` at execution time.
5768
PYBIND11_NOINLINE static void add_patient(handle h) {
58-
auto &stack = get_internals().loader_patient_stack;
59-
if (stack.empty())
69+
loader_life_support* frame = *get_stack_pp();
70+
if (!frame) {
71+
// NOTE: It would be nice to include the stack frames here, as this indicates
72+
// use of pybind11::cast<> outside the normal call framework, finding such
73+
// a location is challenging. Developers could consider printing out
74+
// stack frame addresses here using something like __builtin_frame_address(0)
6075
throw cast_error("When called outside a bound function, py::cast() cannot "
6176
"do Python -> C++ conversions which require the creation "
6277
"of temporary values");
63-
64-
auto &list_ptr = stack.back();
65-
if (list_ptr == nullptr) {
66-
list_ptr = PyList_New(1);
67-
if (!list_ptr)
68-
pybind11_fail("loader_life_support: error allocating list");
69-
PyList_SET_ITEM(list_ptr, 0, h.inc_ref().ptr());
70-
} else {
71-
auto result = PyList_Append(list_ptr, h.ptr());
72-
if (result == -1)
73-
pybind11_fail("loader_life_support: error adding patient");
7478
}
79+
80+
if (frame->keep_alive.insert(h.ptr()).second)
81+
Py_INCREF(h.ptr());
7582
}
7683
};
7784

include/pybind11/eval.h

+9
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ object eval_file(str fname, object global = globals(), object local = object())
136136
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
137137
}
138138

139+
// In Python2, this should be encoded by getfilesystemencoding.
140+
// We don't boher setting it since Python2 is past EOL anyway.
141+
// See PR#3233
142+
#if PY_VERSION_HEX >= 0x03000000
143+
if (!global.contains("__file__")) {
144+
global["__file__"] = std::move(fname);
145+
}
146+
#endif
147+
139148
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
140149
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
141150
local.ptr());

include/pybind11/pybind11.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -2101,6 +2101,7 @@ iterator make_iterator(Iterator first, Sentinel last, Extra &&... extra) {
21012101
throw stop_iteration();
21022102
}
21032103
return *s.it;
2104+
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
21042105
}, std::forward<Extra>(extra)..., Policy);
21052106
}
21062107

@@ -2116,13 +2117,13 @@ template <return_value_policy Policy = return_value_policy::reference_internal,
21162117
typename KeyType = decltype((*std::declval<Iterator>()).first),
21172118
#endif
21182119
typename... Extra>
2119-
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&... extra) {
2120+
iterator make_key_iterator(Iterator first, Sentinel last, Extra &&...extra) {
21202121
using state = detail::iterator_state<Iterator, Sentinel, true, Policy>;
21212122

21222123
if (!detail::get_type_info(typeid(state), false)) {
21232124
class_<state>(handle(), "iterator", pybind11::module_local())
21242125
.def("__iter__", [](state &s) -> state& { return s; })
2125-
.def("__next__", [](state &s) -> KeyType {
2126+
.def("__next__", [](state &s) -> detail::remove_cv_t<KeyType> {
21262127
if (!s.first_or_done)
21272128
++s.it;
21282129
else

include/pybind11/pytypes.h

+15-5
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,9 @@ class generic_iterator : public Policy {
733733
generic_iterator() = default;
734734
generic_iterator(handle seq, ssize_t index) : Policy(seq, index) { }
735735

736+
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
736737
reference operator*() const { return Policy::dereference(); }
738+
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
737739
reference operator[](difference_type n) const { return *(*this + n); }
738740
pointer operator->() const { return **this; }
739741

@@ -773,11 +775,12 @@ class sequence_fast_readonly {
773775
protected:
774776
using iterator_category = std::random_access_iterator_tag;
775777
using value_type = handle;
776-
using reference = const handle;
778+
using reference = const handle; // PR #3263
777779
using pointer = arrow_proxy<const handle>;
778780

779781
sequence_fast_readonly(handle obj, ssize_t n) : ptr(PySequence_Fast_ITEMS(obj.ptr()) + n) { }
780782

783+
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
781784
reference dereference() const { return *ptr; }
782785
void increment() { ++ptr; }
783786
void decrement() { --ptr; }
@@ -816,12 +819,13 @@ class dict_readonly {
816819
protected:
817820
using iterator_category = std::forward_iterator_tag;
818821
using value_type = std::pair<handle, handle>;
819-
using reference = const value_type;
822+
using reference = const value_type; // PR #3263
820823
using pointer = arrow_proxy<const value_type>;
821824

822825
dict_readonly() = default;
823826
dict_readonly(handle obj, ssize_t pos) : obj(obj), pos(pos) { increment(); }
824827

828+
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
825829
reference dereference() const { return {key, value}; }
826830
void increment() {
827831
if (PyDict_Next(obj.ptr(), &pos, &key, &value) == 0) {
@@ -966,7 +970,7 @@ class iterator : public object {
966970
using iterator_category = std::input_iterator_tag;
967971
using difference_type = ssize_t;
968972
using value_type = handle;
969-
using reference = const handle;
973+
using reference = const handle; // PR #3263
970974
using pointer = const handle *;
971975

972976
PYBIND11_OBJECT_DEFAULT(iterator, object, PyIter_Check)
@@ -982,6 +986,7 @@ class iterator : public object {
982986
return rv;
983987
}
984988

989+
// NOLINTNEXTLINE(readability-const-return-type) // PR #3263
985990
reference operator*() const {
986991
if (m_ptr && !value.ptr()) {
987992
auto& self = const_cast<iterator &>(*this);
@@ -1414,14 +1419,19 @@ class capsule : public object {
14141419
T* get_pointer() const {
14151420
auto name = this->name();
14161421
T *result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, name));
1417-
if (!result) pybind11_fail("Unable to extract capsule contents!");
1422+
if (!result) {
1423+
PyErr_Clear();
1424+
pybind11_fail("Unable to extract capsule contents!");
1425+
}
14181426
return result;
14191427
}
14201428

14211429
/// Replaces a capsule's pointer *without* calling the destructor on the existing one.
14221430
void set_pointer(const void *value) {
1423-
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0)
1431+
if (PyCapsule_SetPointer(m_ptr, const_cast<void *>(value)) != 0) {
1432+
PyErr_Clear();
14241433
pybind11_fail("Could not set capsule pointer");
1434+
}
14251435
}
14261436

14271437
const char *name() const { return PyCapsule_GetName(m_ptr); }

pybind11/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ def _to_int(s):
88
return s
99

1010

11-
__version__ = "2.8.0.dev1"
11+
__version__ = "2.8.0.dev2"
1212
version_info = tuple(_to_int(s) for s in __version__.split("."))

tests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ set(PYBIND11_TEST_FILES
143143
test_stl.cpp
144144
test_stl_binders.cpp
145145
test_tagbased_polymorphic.cpp
146+
test_thread.cpp
146147
test_union.cpp
147148
test_virtual_functions.cpp)
148149

tests/test_cmake_build/test.py

+3
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@
33

44
import test_cmake_build
55

6+
if str is not bytes: # If not Python2
7+
assert isinstance(__file__, str) # Test this is properly set
8+
69
assert test_cmake_build.add(1, 2) == 3
710
print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1]))

tests/test_eigen.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ TEST_SUBMODULE(eigen, m) {
178178
ReturnTester() { print_created(this); }
179179
~ReturnTester() { print_destroyed(this); }
180180
static Eigen::MatrixXd create() { return Eigen::MatrixXd::Ones(10, 10); }
181+
// NOLINTNEXTLINE(readability-const-return-type)
181182
static const Eigen::MatrixXd createConst() { return Eigen::MatrixXd::Ones(10, 10); }
182183
Eigen::MatrixXd &get() { return mat; }
183184
Eigen::MatrixXd *getPtr() { return &mat; }
@@ -244,6 +245,9 @@ TEST_SUBMODULE(eigen, m) {
244245

245246
// test_fixed, and various other tests
246247
m.def("fixed_r", [mat]() -> FixedMatrixR { return FixedMatrixR(mat); });
248+
// Our Eigen does a hack which respects constness through the numpy writeable flag.
249+
// Therefore, the const return actually affects this type despite being an rvalue.
250+
// NOLINTNEXTLINE(readability-const-return-type)
247251
m.def("fixed_r_const", [mat]() -> const FixedMatrixR { return FixedMatrixR(mat); });
248252
m.def("fixed_c", [mat]() -> FixedMatrixC { return FixedMatrixC(mat); });
249253
m.def("fixed_copy_r", [](const FixedMatrixR &m) -> FixedMatrixR { return m; });

tests/test_pytypes.cpp

+43
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,49 @@ TEST_SUBMODULE(pytypes, m) {
462462
m.def("weakref_from_object_and_function",
463463
[](py::object o, py::function f) { return py::weakref(std::move(o), std::move(f)); });
464464

465+
// See PR #3263 for background (https://github.com/pybind/pybind11/pull/3263):
466+
// pytypes.h could be changed to enforce the "most correct" user code below, by removing
467+
// `const` from iterator `reference` using type aliases, but that will break existing
468+
// user code.
469+
#if (defined(__APPLE__) && defined(__clang__)) || defined(PYPY_VERSION)
470+
// This is "most correct" and enforced on these platforms.
471+
# define PYBIND11_AUTO_IT auto it
472+
#else
473+
// This works on many platforms and is (unfortunately) reflective of existing user code.
474+
// NOLINTNEXTLINE(bugprone-macro-parentheses)
475+
# define PYBIND11_AUTO_IT auto &it
476+
#endif
477+
478+
m.def("tuple_iterator", []() {
479+
auto tup = py::make_tuple(5, 7);
480+
int tup_sum = 0;
481+
for (PYBIND11_AUTO_IT : tup) {
482+
tup_sum += it.cast<int>();
483+
}
484+
return tup_sum;
485+
});
486+
487+
m.def("dict_iterator", []() {
488+
py::dict dct;
489+
dct[py::int_(3)] = 5;
490+
dct[py::int_(7)] = 11;
491+
int kv_sum = 0;
492+
for (PYBIND11_AUTO_IT : dct) {
493+
kv_sum += it.first.cast<int>() * 100 + it.second.cast<int>();
494+
}
495+
return kv_sum;
496+
});
497+
498+
m.def("passed_iterator", [](const py::iterator &py_it) {
499+
int elem_sum = 0;
500+
for (PYBIND11_AUTO_IT : py_it) {
501+
elem_sum += it.cast<int>();
502+
}
503+
return elem_sum;
504+
});
505+
506+
#undef PYBIND11_AUTO_IT
507+
465508
// Tests below this line are for pybind11 IMPLEMENTATION DETAILS:
466509

467510
m.def("sequence_item_get_ssize_t", [](const py::object &o) {

0 commit comments

Comments
 (0)