@@ -18,6 +18,7 @@ limitations under the License.
1818#pragma once
1919
2020#include < exception> // std::rethrow_exception, std::current_exception
21+ #include < format> // std::format, std::format_to, std::formatter
2122#include < string> // std::string
2223#include < type_traits> // std::is_base_of_v
2324#include < unordered_map> // std::unordered_map
@@ -49,6 +50,29 @@ inline Py_ALWAYS_INLINE std::string PyRepr(const std::string &string) {
4950 return static_cast <std::string>(py::repr (py::str (string)));
5051}
5152
53+ template <typename CharT>
54+ struct std ::formatter<py::handle, CharT> {
55+ template <class ParseContext >
56+ constexpr ParseContext::iterator parse (const ParseContext &context) {
57+ return context.begin ();
58+ }
59+ template <class FormatContext >
60+ FormatContext::iterator format (const py::handle &object, FormatContext &context) const {
61+ return std::format_to (context.out (), " {}" , PyRepr (object));
62+ }
63+ };
64+ template <typename CharT>
65+ struct std ::formatter<py::object, CharT> {
66+ template <class ParseContext >
67+ constexpr ParseContext::iterator parse (const ParseContext &context) {
68+ return context.begin ();
69+ }
70+ template <class FormatContext >
71+ FormatContext::iterator format (const py::object &object, FormatContext &context) const {
72+ return std::format_to (context.out (), " {}" , PyRepr (object));
73+ }
74+ };
75+
5276// The maximum size of the type cache.
5377constexpr py::ssize_t MAX_TYPE_CACHE_SIZE = 4096 ;
5478
@@ -173,31 +197,31 @@ inline Py_ALWAYS_INLINE void DictSetItem(const py::handle &dict,
173197
174198inline Py_ALWAYS_INLINE void AssertExactList (const py::handle &object) {
175199 if (!PyList_CheckExact (object.ptr ())) [[unlikely]] {
176- throw py::value_error (" Expected an instance of list, got " + PyRepr ( object) + " . " );
200+ throw py::value_error (std::format ( " Expected an instance of list, got {}. " , object));
177201 }
178202}
179203inline Py_ALWAYS_INLINE void AssertExactTuple (const py::handle &object) {
180204 if (!PyTuple_CheckExact (object.ptr ())) [[unlikely]] {
181- throw py::value_error (" Expected an instance of tuple, got " + PyRepr ( object) + " . " );
205+ throw py::value_error (std::format ( " Expected an instance of tuple, got {}. " , object));
182206 }
183207}
184208inline Py_ALWAYS_INLINE void AssertExactDict (const py::handle &object) {
185209 if (!PyDict_CheckExact (object.ptr ())) [[unlikely]] {
186- throw py::value_error (" Expected an instance of dict, got " + PyRepr ( object) + " . " );
210+ throw py::value_error (std::format ( " Expected an instance of dict, got {}. " , object));
187211 }
188212}
189213
190214inline Py_ALWAYS_INLINE void AssertExactOrderedDict (const py::handle &object) {
191215 if (!py::type::handle_of (object).is (PyOrderedDictTypeObject)) [[unlikely]] {
192- throw py::value_error (" Expected an instance of collections.OrderedDict, got " +
193- PyRepr ( object) + " . " );
216+ throw py::value_error (
217+ std::format ( " Expected an instance of collections.OrderedDict, got {}. " , object));
194218 }
195219}
196220
197221inline Py_ALWAYS_INLINE void AssertExactDefaultDict (const py::handle &object) {
198222 if (!py::type::handle_of (object).is (PyDefaultDictTypeObject)) [[unlikely]] {
199- throw py::value_error (" Expected an instance of collections.defaultdict, got " +
200- PyRepr ( object) + " . " );
223+ throw py::value_error (
224+ std::format ( " Expected an instance of collections.defaultdict, got {}. " , object));
201225 }
202226}
203227
@@ -206,16 +230,16 @@ inline Py_ALWAYS_INLINE void AssertExactStandardDict(const py::handle &object) {
206230 py::type::handle_of (object).is (PyOrderedDictTypeObject) ||
207231 py::type::handle_of (object).is (PyDefaultDictTypeObject))) [[unlikely]] {
208232 throw py::value_error (
209- " Expected an instance of dict, collections.OrderedDict, or collections.defaultdict , "
210- " got " +
211- PyRepr (object) + " . " );
233+ std::format ( " Expected an instance of dict, collections.OrderedDict, "
234+ " or collections.defaultdict, got {}. " ,
235+ object) );
212236 }
213237}
214238
215239inline Py_ALWAYS_INLINE void AssertExactDeque (const py::handle &object) {
216240 if (!py::type::handle_of (object).is (PyDequeTypeObject)) [[unlikely]] {
217- throw py::value_error (" Expected an instance of collections.deque, got " + PyRepr (object) +
218- " . " );
241+ throw py::value_error (
242+ std::format ( " Expected an instance of collections.deque, got {}. " , object) );
219243 }
220244}
221245
@@ -298,23 +322,24 @@ inline Py_ALWAYS_INLINE bool IsNamedTuple(const py::handle &object) {
298322}
299323inline Py_ALWAYS_INLINE void AssertExactNamedTuple (const py::handle &object) {
300324 if (!IsNamedTupleInstance (object)) [[unlikely]] {
301- throw py::value_error (" Expected an instance of collections.namedtuple, got " +
302- PyRepr ( object) + " . " );
325+ throw py::value_error (
326+ std::format ( " Expected an instance of collections.namedtuple, got {}. " , object));
303327 }
304328}
305329inline py::tuple NamedTupleGetFields (const py::handle &object) {
306330 py::handle type;
307331 if (PyType_Check (object.ptr ())) [[unlikely]] {
308332 type = object;
309333 if (!IsNamedTupleClass (type)) [[unlikely]] {
310- throw py::type_error (" Expected a collections.namedtuple type, got " + PyRepr (object) +
311- " . " );
334+ throw py::type_error (
335+ std::format ( " Expected a collections.namedtuple type, got {}. " , object) );
312336 }
313337 } else [[likely]] {
314338 type = py::type::handle_of (object);
315339 if (!IsNamedTupleClass (type)) [[unlikely]] {
316- throw py::type_error (" Expected an instance of collections.namedtuple type, got " +
317- PyRepr (object) + " ." );
340+ throw py::type_error (
341+ std::format (" Expected an instance of collections.namedtuple type, got {}." ,
342+ object));
318343 }
319344 }
320345 return EVALUATE_WITH_LOCK_HELD (py::getattr (type, Py_Get_ID (_fields)), type);
@@ -401,8 +426,8 @@ inline Py_ALWAYS_INLINE bool IsStructSequence(const py::handle &object) {
401426}
402427inline Py_ALWAYS_INLINE void AssertExactStructSequence (const py::handle &object) {
403428 if (!IsStructSequenceInstance (object)) [[unlikely]] {
404- throw py::value_error (" Expected an instance of PyStructSequence type, got " +
405- PyRepr ( object) + " . " );
429+ throw py::value_error (
430+ std::format ( " Expected an instance of PyStructSequence type, got {}. " , object));
406431 }
407432}
408433inline py::tuple StructSequenceGetFieldsImpl (const py::handle &type) {
@@ -439,13 +464,13 @@ inline py::tuple StructSequenceGetFields(const py::handle &object) {
439464 if (PyType_Check (object.ptr ())) [[unlikely]] {
440465 type = object;
441466 if (!IsStructSequenceClass (type)) [[unlikely]] {
442- throw py::type_error (" Expected a PyStructSequence type, got " + PyRepr ( object) + " . " );
467+ throw py::type_error (std::format ( " Expected a PyStructSequence type, got {}. " , object));
443468 }
444469 } else [[likely]] {
445470 type = py::type::handle_of (object);
446471 if (!IsStructSequenceClass (type)) [[unlikely]] {
447- throw py::type_error (" Expected an instance of PyStructSequence type, got " +
448- PyRepr ( object) + " . " );
472+ throw py::type_error (
473+ std::format ( " Expected an instance of PyStructSequence type, got {}. " , object));
449474 }
450475 }
451476
@@ -496,8 +521,9 @@ inline void TotalOrderSort(py::list &list) { // NOLINT[runtime/references]
496521 const auto sort_key_fn = py::cpp_function ([](const py::object &obj) -> py::tuple {
497522 const py::handle cls = py::type::handle_of (obj);
498523 const py::str qualname{EVALUATE_WITH_LOCK_HELD (
499- PyStr (py::getattr (cls, Py_Get_ID (__module__))) + " ." +
500- PyStr (py::getattr (cls, Py_Get_ID (__qualname__))),
524+ std::format (" {}.{}" ,
525+ PyStr (py::getattr (cls, Py_Get_ID (__module__))),
526+ PyStr (py::getattr (cls, Py_Get_ID (__qualname__)))),
501527 cls)};
502528 return py::make_tuple (qualname, obj);
503529 });
0 commit comments