Skip to content

Commit d2e78b0

Browse files
committed
Merge branch 'master' into lazy-error-string
2 parents 9854589 + 58802de commit d2e78b0

File tree

5 files changed

+97
-12
lines changed

5 files changed

+97
-12
lines changed

include/pybind11/pybind11.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,12 +2069,12 @@ struct enum_base {
20692069
str name(name_);
20702070
if (entries.contains(name)) {
20712071
std::string type_name = (std::string) str(m_base.attr("__name__"));
2072-
throw value_error(type_name + ": element \"" + std::string(name_)
2072+
throw value_error(std::move(type_name) + ": element \"" + std::string(name_)
20732073
+ "\" already exists!");
20742074
}
20752075

20762076
entries[name] = std::make_pair(value, doc);
2077-
m_base.attr(name) = value;
2077+
m_base.attr(std::move(name)) = std::move(value);
20782078
}
20792079

20802080
PYBIND11_NOINLINE void export_values() {
@@ -2610,7 +2610,7 @@ PYBIND11_NOINLINE void print(const tuple &args, const dict &kwargs) {
26102610
}
26112611

26122612
auto write = file.attr("write");
2613-
write(line);
2613+
write(std::move(line));
26142614
write(kwargs.contains("end") ? kwargs["end"] : str("\n"));
26152615

26162616
if (kwargs.contains("flush") && kwargs["flush"].cast<bool>()) {

include/pybind11/pytypes.h

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ class object_api : public pyobject_tag {
8888
or `object` subclass causes a call to ``__setitem__``.
8989
\endrst */
9090
item_accessor operator[](handle key) const;
91-
/// See above (the only difference is that they key is provided as a string literal)
91+
/// See above (the only difference is that the key's reference is stolen)
92+
item_accessor operator[](object &&key) const;
93+
/// See above (the only difference is that the key is provided as a string literal)
9294
item_accessor operator[](const char *key) const;
9395

9496
/** \rst
@@ -98,7 +100,9 @@ class object_api : public pyobject_tag {
98100
or `object` subclass causes a call to ``setattr``.
99101
\endrst */
100102
obj_attr_accessor attr(handle key) const;
101-
/// See above (the only difference is that they key is provided as a string literal)
103+
/// See above (the only difference is that the key's reference is stolen)
104+
obj_attr_accessor attr(object &&key) const;
105+
/// See above (the only difference is that the key is provided as a string literal)
102106
str_attr_accessor attr(const char *key) const;
103107

104108
/** \rst
@@ -863,7 +867,7 @@ class accessor : public object_api<accessor<Policy>> {
863867
}
864868
template <typename T>
865869
void operator=(T &&value) & {
866-
get_cache() = reinterpret_borrow<object>(object_or_cast(std::forward<T>(value)));
870+
get_cache() = ensure_object(object_or_cast(std::forward<T>(value)));
867871
}
868872

869873
template <typename T = Policy>
@@ -891,6 +895,9 @@ class accessor : public object_api<accessor<Policy>> {
891895
}
892896

893897
private:
898+
static object ensure_object(object &&o) { return std::move(o); }
899+
static object ensure_object(handle h) { return reinterpret_borrow<object>(h); }
900+
894901
object &get_cache() const {
895902
if (!cache) {
896903
cache = Policy::get(obj, key);
@@ -1890,7 +1897,10 @@ class tuple : public object {
18901897
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
18911898
bool empty() const { return size() == 0; }
18921899
detail::tuple_accessor operator[](size_t index) const { return {*this, index}; }
1893-
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
1900+
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
1901+
detail::item_accessor operator[](T &&o) const {
1902+
return object::operator[](std::forward<T>(o));
1903+
}
18941904
detail::tuple_iterator begin() const { return {*this, 0}; }
18951905
detail::tuple_iterator end() const { return {*this, PyTuple_GET_SIZE(m_ptr)}; }
18961906
};
@@ -1950,7 +1960,10 @@ class sequence : public object {
19501960
}
19511961
bool empty() const { return size() == 0; }
19521962
detail::sequence_accessor operator[](size_t index) const { return {*this, index}; }
1953-
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
1963+
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
1964+
detail::item_accessor operator[](T &&o) const {
1965+
return object::operator[](std::forward<T>(o));
1966+
}
19541967
detail::sequence_iterator begin() const { return {*this, 0}; }
19551968
detail::sequence_iterator end() const { return {*this, PySequence_Size(m_ptr)}; }
19561969
};
@@ -1969,7 +1982,10 @@ class list : public object {
19691982
size_t size() const { return (size_t) PyList_Size(m_ptr); }
19701983
bool empty() const { return size() == 0; }
19711984
detail::list_accessor operator[](size_t index) const { return {*this, index}; }
1972-
detail::item_accessor operator[](handle h) const { return object::operator[](h); }
1985+
template <typename T, detail::enable_if_t<detail::is_pyobject<T>::value, int> = 0>
1986+
detail::item_accessor operator[](T &&o) const {
1987+
return object::operator[](std::forward<T>(o));
1988+
}
19731989
detail::list_iterator begin() const { return {*this, 0}; }
19741990
detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; }
19751991
template <typename T>
@@ -2269,6 +2285,10 @@ item_accessor object_api<D>::operator[](handle key) const {
22692285
return {derived(), reinterpret_borrow<object>(key)};
22702286
}
22712287
template <typename D>
2288+
item_accessor object_api<D>::operator[](object &&key) const {
2289+
return {derived(), std::move(key)};
2290+
}
2291+
template <typename D>
22722292
item_accessor object_api<D>::operator[](const char *key) const {
22732293
return {derived(), pybind11::str(key)};
22742294
}
@@ -2277,6 +2297,10 @@ obj_attr_accessor object_api<D>::attr(handle key) const {
22772297
return {derived(), reinterpret_borrow<object>(key)};
22782298
}
22792299
template <typename D>
2300+
obj_attr_accessor object_api<D>::attr(object &&key) const {
2301+
return {derived(), std::move(key)};
2302+
}
2303+
template <typename D>
22802304
str_attr_accessor object_api<D>::attr(const char *key) const {
22812305
return {derived(), key};
22822306
}

include/pybind11/stl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ struct map_caster {
128128
if (!key || !value) {
129129
return handle();
130130
}
131-
d[key] = value;
131+
d[std::move(key)] = std::move(value);
132132
}
133133
return d.release();
134134
}

tests/test_pytypes.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,4 +661,38 @@ TEST_SUBMODULE(pytypes, m) {
661661
double v = x.get_value();
662662
return v * v;
663663
});
664+
665+
m.def("tuple_rvalue_getter", [](const py::tuple &tup) {
666+
// tests accessing tuple object with rvalue int
667+
for (size_t i = 0; i < tup.size(); i++) {
668+
auto o = py::handle(tup[py::int_(i)]);
669+
if (!o) {
670+
throw py::value_error("tuple is malformed");
671+
}
672+
}
673+
return tup;
674+
});
675+
m.def("list_rvalue_getter", [](const py::list &l) {
676+
// tests accessing list with rvalue int
677+
for (size_t i = 0; i < l.size(); i++) {
678+
auto o = py::handle(l[py::int_(i)]);
679+
if (!o) {
680+
throw py::value_error("list is malformed");
681+
}
682+
}
683+
return l;
684+
});
685+
m.def("populate_dict_rvalue", [](int population) {
686+
auto d = py::dict();
687+
for (int i = 0; i < population; i++) {
688+
d[py::int_(i)] = py::int_(i);
689+
}
690+
return d;
691+
});
692+
m.def("populate_obj_str_attrs", [](py::object &o, int population) {
693+
for (int i = 0; i < population; i++) {
694+
o.attr(py::str(py::int_(i))) = py::str(py::int_(i));
695+
}
696+
return o;
697+
});
664698
}

tests/test_pytypes.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import contextlib
22
import sys
3+
import types
34

45
import pytest
56

@@ -320,8 +321,7 @@ def func(self, x, *args):
320321
def test_accessor_moves():
321322
inc_refs = m.accessor_moves()
322323
if inc_refs:
323-
# To be changed in PR #3970: [1, 0, 1, 0, ...]
324-
assert inc_refs == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
324+
assert inc_refs == [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
325325
else:
326326
pytest.skip("Not defined: PYBIND11_HANDLE_REF_DEBUG")
327327

@@ -707,3 +707,30 @@ def test_implementation_details():
707707
def test_external_float_():
708708
r1 = m.square_float_(2.0)
709709
assert r1 == 4.0
710+
711+
712+
def test_tuple_rvalue_getter():
713+
pop = 1000
714+
tup = tuple(range(pop))
715+
m.tuple_rvalue_getter(tup)
716+
717+
718+
def test_list_rvalue_getter():
719+
pop = 1000
720+
my_list = list(range(pop))
721+
m.list_rvalue_getter(my_list)
722+
723+
724+
def test_populate_dict_rvalue():
725+
pop = 1000
726+
my_dict = {i: i for i in range(pop)}
727+
assert m.populate_dict_rvalue(pop) == my_dict
728+
729+
730+
def test_populate_obj_str_attrs():
731+
pop = 1000
732+
o = types.SimpleNamespace(**{str(i): i for i in range(pop)})
733+
new_o = m.populate_obj_str_attrs(o, pop)
734+
new_attrs = {k: v for k, v in new_o.__dict__.items() if not k.startswith("_")}
735+
assert all(isinstance(v, str) for v in new_attrs.values())
736+
assert len(new_attrs) == pop

0 commit comments

Comments
 (0)