diff --git a/dune/pybindxi/attr.h b/dune/pybindxi/attr.h
index ae5df633e0d174b3b590f0249058bbfbdc4eaf4b..378b7a804495def3b89e23bae88f11a119848567 100644
--- a/dune/pybindxi/attr.h
+++ b/dune/pybindxi/attr.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/attr.h: Infrastructure for processing custom
     type and function attributes
diff --git a/dune/pybindxi/buffer_info.h b/dune/pybindxi/buffer_info.h
index 925133c379dfd3f2af67995424db9f83e5aebbc4..cd436083c3c8eb66dbff3e2979ad7eae75e194f5 100644
--- a/dune/pybindxi/buffer_info.h
+++ b/dune/pybindxi/buffer_info.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/buffer_info.h: Python buffer object interface
 
@@ -23,7 +22,8 @@ struct buffer_info
   std::string format; // For homogeneous buffers, this should be set to format_descriptor<T>::format()
   ssize_t ndim = 0; // Number of dimensions
   std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
-  std::vector<ssize_t> strides; // Number of entries between adjacent entries (for each per dimension)
+  std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
+  bool readonly = false; // flag to indicate if the underlying storage may be written to
 
   buffer_info() {}
 
@@ -32,7 +32,8 @@ struct buffer_info
               const std::string& format,
               ssize_t ndim,
               detail::any_container<ssize_t> shape_in,
-              detail::any_container<ssize_t> strides_in)
+              detail::any_container<ssize_t> strides_in,
+              bool readonly = false)
     : ptr(ptr)
     , itemsize(itemsize)
     , size(1)
@@ -40,6 +41,7 @@ struct buffer_info
     , ndim(ndim)
     , shape(std::move(shape_in))
     , strides(std::move(strides_in))
+    , readonly(readonly)
   {
     if (ndim != (ssize_t)shape.size() || ndim != (ssize_t)strides.size())
       pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
@@ -48,23 +50,32 @@ struct buffer_info
   }
 
   template <typename T>
-  buffer_info(T* ptr, detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in)
+  buffer_info(T* ptr,
+              detail::any_container<ssize_t> shape_in,
+              detail::any_container<ssize_t> strides_in,
+              bool readonly = false)
     : buffer_info(private_ctr_tag(),
                   ptr,
                   sizeof(T),
                   format_descriptor<T>::format(),
                   static_cast<ssize_t>(shape_in->size()),
                   std::move(shape_in),
-                  std::move(strides_in))
+                  std::move(strides_in),
+                  readonly)
+  {}
+
+  buffer_info(void* ptr, ssize_t itemsize, const std::string& format, ssize_t size, bool readonly = false)
+    : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly)
   {}
 
-  buffer_info(void* ptr, ssize_t itemsize, const std::string& format, ssize_t size)
-    : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize})
+  template <typename T>
+  buffer_info(T* ptr, ssize_t size, bool readonly = false)
+    : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly)
   {}
 
   template <typename T>
-  buffer_info(T* ptr, ssize_t size)
-    : buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size)
+  buffer_info(const T* ptr, ssize_t size, bool readonly = true)
+    : buffer_info(const_cast<T*>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly)
   {}
 
   explicit buffer_info(Py_buffer* view, bool ownview = true)
@@ -73,7 +84,8 @@ struct buffer_info
                   view->format,
                   view->ndim,
                   {view->shape, view->shape + view->ndim},
-                  {view->strides, view->strides + view->ndim})
+                  {view->strides, view->strides + view->ndim},
+                  view->readonly)
   {
     this->view = view;
     this->ownview = ownview;
@@ -98,6 +110,7 @@ struct buffer_info
     strides = std::move(rhs.strides);
     std::swap(view, rhs.view);
     std::swap(ownview, rhs.ownview);
+    readonly = rhs.readonly;
     return *this;
   }
 
@@ -119,8 +132,9 @@ private:
               const std::string& format,
               ssize_t ndim,
               detail::any_container<ssize_t>&& shape_in,
-              detail::any_container<ssize_t>&& strides_in)
-    : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in))
+              detail::any_container<ssize_t>&& strides_in,
+              bool readonly)
+    : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly)
   {}
 
   Py_buffer* view = nullptr;
diff --git a/dune/pybindxi/cast.h b/dune/pybindxi/cast.h
index e09413c51a5dbcf171220bb9f3cf36f0cc020880..641ed21068138b04a32b125ad3cb46f859b93a11 100644
--- a/dune/pybindxi/cast.h
+++ b/dune/pybindxi/cast.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/cast.h: Partial template specializations to cast between
     C++ and Python types
@@ -33,6 +32,10 @@
 #  include <string_view>
 #endif
 
+#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L
+#  define PYBIND11_HAS_U8STRING
+#endif
+
 NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
 NAMESPACE_BEGIN(detail)
 
@@ -610,9 +613,16 @@ public:
       case return_value_policy::copy:
         if (copy_constructor)
           valueptr = copy_constructor(src);
-        else
-          throw cast_error("return_value_policy = copy, but the "
-                           "object is non-copyable!");
+        else {
+#if defined(NDEBUG)
+          throw cast_error("return_value_policy = copy, but type is "
+                           "non-copyable! (compile in debug mode for details)");
+#else
+          std::string type_name(tinfo->cpptype->name());
+          detail::clean_type_id(type_name);
+          throw cast_error("return_value_policy = copy, but type " + type_name + " is non-copyable!");
+#endif
+        }
         wrapper->owned = true;
         break;
 
@@ -621,9 +631,17 @@ public:
           valueptr = move_constructor(src);
         else if (copy_constructor)
           valueptr = copy_constructor(src);
-        else
-          throw cast_error("return_value_policy = move, but the "
-                           "object is neither movable nor copyable!");
+        else {
+#if defined(NDEBUG)
+          throw cast_error("return_value_policy = move, but type is neither "
+                           "movable nor copyable! "
+                           "(compile in debug mode for details)");
+#else
+          std::string type_name(tinfo->cpptype->name());
+          detail::clean_type_id(type_name);
+          throw cast_error("return_value_policy = move, but type " + type_name + " is neither movable nor copyable!");
+#endif
+        }
         wrapper->owned = true;
         break;
 
@@ -652,9 +670,9 @@ public:
       if (type->operator_new) {
         vptr = type->operator_new(type->type_size);
       } else {
-#if defined(PYBIND11_CPP17)
+#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
         if (type->type_align > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
-          vptr = ::operator new(type->type_size, (std::align_val_t)type->type_align);
+          vptr = ::operator new(type->type_size, std::align_val_t(type->type_align));
         else
 #endif
           vptr = ::operator new(type->type_size);
@@ -863,17 +881,33 @@ template <typename Container>
 struct is_copy_constructible<
     Container,
     enable_if_t<all_of<std::is_copy_constructible<Container>,
-                       std::is_same<typename Container::value_type&, typename Container::reference>>::value>>
+                       std::is_same<typename Container::value_type&, typename Container::reference>,
+                       // Avoid infinite recursion
+                       negation<std::is_same<Container, typename Container::value_type>>>::value>>
   : is_copy_constructible<typename Container::value_type>
 {};
 
-#if !defined(PYBIND11_CPP17)
-// Likewise for std::pair before C++17 (which mandates that the copy constructor not exist when the
-// two types aren't themselves copy constructible).
+// Likewise for std::pair
+// (after C++17 it is mandatory that the copy constructor not exist when the two types aren't themselves
+// copy constructible, but this can not be relied upon when T1 or T2 are themselves containers).
 template <typename T1, typename T2>
 struct is_copy_constructible<std::pair<T1, T2>> : all_of<is_copy_constructible<T1>, is_copy_constructible<T2>>
 {};
-#endif
+
+// The same problems arise with std::is_copy_assignable, so we use the same workaround.
+template <typename T, typename SFINAE = void>
+struct is_copy_assignable : std::is_copy_assignable<T>
+{};
+template <typename Container>
+struct is_copy_assignable<
+    Container,
+    enable_if_t<all_of<std::is_copy_assignable<Container>,
+                       std::is_same<typename Container::value_type&, typename Container::reference>>::value>>
+  : is_copy_assignable<typename Container::value_type>
+{};
+template <typename T1, typename T2>
+struct is_copy_assignable<std::pair<T1, T2>> : all_of<is_copy_assignable<T1>, is_copy_assignable<T2>>
+{};
 
 NAMESPACE_END(detail)
 
@@ -1110,6 +1144,9 @@ public:
 
 template <typename CharT>
 using is_std_char_type = any_of<std::is_same<CharT, char>, /* std::string */
+#if defined(PYBIND11_HAS_U8STRING)
+                                std::is_same<CharT, char8_t>, /* std::u8string */
+#endif
                                 std::is_same<CharT, char16_t>, /* std::u16string */
                                 std::is_same<CharT, char32_t>, /* std::u32string */
                                 std::is_same<CharT, wchar_t> /* std::wstring */
@@ -1145,10 +1182,12 @@ public:
     }
 
     bool py_err = py_value == (py_type)-1 && PyErr_Occurred();
+
+    // Protect std::numeric_limits::min/max with parentheses
     if (py_err
         || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T)
-            && (py_value < (py_type)std::numeric_limits<T>::min()
-                || py_value > (py_type)std::numeric_limits<T>::max()))) {
+            && (py_value < (py_type)(std::numeric_limits<T>::min)()
+                || py_value > (py_type)(std::numeric_limits<T>::max)()))) {
       bool type_error = py_err
                         && PyErr_ExceptionMatches(
 #if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION)
@@ -1332,6 +1371,8 @@ public:
       if (res == 0 || res == 1) {
         value = (bool)res;
         return true;
+      } else {
+        PyErr_Clear();
       }
     }
     return false;
@@ -1352,6 +1393,9 @@ struct string_caster
   // Simplify life by being able to assume standard char sizes (the standard only guarantees
   // minimums, but Python requires exact sizes)
   static_assert(!std::is_same<CharT, char>::value || sizeof(CharT) == 1, "Unsupported char size != 1");
+#if defined(PYBIND11_HAS_U8STRING)
+  static_assert(!std::is_same<CharT, char8_t>::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1");
+#endif
   static_assert(!std::is_same<CharT, char16_t>::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2");
   static_assert(!std::is_same<CharT, char32_t>::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4");
   // wchar_t can be either 16 bits (Windows) or 32 (everywhere else)
@@ -1371,7 +1415,7 @@ struct string_caster
 #if PY_MAJOR_VERSION >= 3
       return load_bytes(load_src);
 #else
-      if (sizeof(CharT) == 1) {
+      if (std::is_same<CharT, char>::value) {
         return load_bytes(load_src);
       }
 
@@ -1442,7 +1486,7 @@ private:
   // without any encoding/decoding attempt).  For other C++ char sizes this is a no-op.
   // which supports loading a unicode from a str, doesn't take this path.
   template <typename C = CharT>
-  bool load_bytes(enable_if_t<sizeof(C) == 1, handle> src)
+  bool load_bytes(enable_if_t<std::is_same<C, char>::value, handle> src)
   {
     if (PYBIND11_BYTES_CHECK(src.ptr())) {
       // We were passed a Python 3 raw bytes; accept it into a std::string or char*
@@ -1458,7 +1502,7 @@ private:
   }
 
   template <typename C = CharT>
-  bool load_bytes(enable_if_t<sizeof(C) != 1, handle>)
+  bool load_bytes(enable_if_t<!std::is_same<C, char>::value, handle>)
   {
     return false;
   }
@@ -1637,9 +1681,14 @@ protected:
   template <size_t... Is>
   bool load_impl(const sequence& seq, bool convert, index_sequence<Is...>)
   {
+#ifdef __cpp_fold_expressions
+    if ((... || !std::get<Is>(subcasters).load(seq[Is], convert)))
+      return false;
+#else
     for (bool r : {std::get<Is>(subcasters).load(seq[Is], convert)...})
       if (!r)
         return false;
+#endif
     return true;
   }
 
@@ -2352,14 +2401,19 @@ private:
   template <size_t... Is>
   bool load_impl_sequence(function_call& call, index_sequence<Is...>)
   {
+#ifdef __cpp_fold_expressions
+    if ((... || !std::get<Is>(argcasters).load(call.args[Is], call.args_convert[Is])))
+      return false;
+#else
     for (bool r : {std::get<Is>(argcasters).load(call.args[Is], call.args_convert[Is])...})
       if (!r)
         return false;
+#endif
     return true;
   }
 
   template <typename Return, typename Func, size_t... Is, typename Guard>
-  Return call_impl(Func&& f, index_sequence<Is...>, Guard&&)
+  Return call_impl(Func&& f, index_sequence<Is...>, Guard&&) &&
   {
     return std::forward<Func>(f)(cast_op<Args>(std::move(std::get<Is>(argcasters)))...);
   }
diff --git a/dune/pybindxi/chrono.h b/dune/pybindxi/chrono.h
index df72db32f6c1dfac5f708bf794857de2f9261aa7..9744d171cd1f25bb63c983b4faf43a81d15aa458 100644
--- a/dune/pybindxi/chrono.h
+++ b/dune/pybindxi/chrono.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime
 
@@ -125,8 +124,11 @@ public:
 
     if (!src)
       return false;
+
+    std::tm cal;
+    microseconds msecs;
+
     if (PyDateTime_Check(src.ptr())) {
-      std::tm cal;
       cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
       cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
       cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
@@ -134,11 +136,30 @@ public:
       cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
       cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
       cal.tm_isdst = -1;
-
-      value = system_clock::from_time_t(std::mktime(&cal)) + microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
-      return true;
+      msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
+    } else if (PyDate_Check(src.ptr())) {
+      cal.tm_sec = 0;
+      cal.tm_min = 0;
+      cal.tm_hour = 0;
+      cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
+      cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
+      cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
+      cal.tm_isdst = -1;
+      msecs = microseconds(0);
+    } else if (PyTime_Check(src.ptr())) {
+      cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
+      cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
+      cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
+      cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
+      cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
+      cal.tm_year = 70; // earliest available date for Python's datetime
+      cal.tm_isdst = -1;
+      msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
     } else
       return false;
+
+    value = system_clock::from_time_t(std::mktime(&cal)) + msecs;
+    return true;
   }
 
   static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration>& src,
diff --git a/dune/pybindxi/common.h b/dune/pybindxi/common.h
index 3dfa83604d10f8cbbc7f220fec47d2986fac3df9..6c8a4f1e88e493ee08d24e668639c8d495fd49b1 100644
--- a/dune/pybindxi/common.h
+++ b/dune/pybindxi/common.h
@@ -1,3 +1,2 @@
-#pragma GCC system_header
 #include "detail/common.h"
 #warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'."
diff --git a/dune/pybindxi/complex.h b/dune/pybindxi/complex.h
index 97e839be39a1066adf07a995bd212ed8763995fb..cd496ccfd8262361bae0d14b9544ba85766f82bd 100644
--- a/dune/pybindxi/complex.h
+++ b/dune/pybindxi/complex.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/complex.h: Complex number support
 
diff --git a/dune/pybindxi/detail/class.h b/dune/pybindxi/detail/class.h
index 2f2945b5f3ce8b3d8488382546a1541cf86eeecf..49c0921753d9c9bc2c52554b3d6537adaa54055a 100644
--- a/dune/pybindxi/detail/class.h
+++ b/dune/pybindxi/detail/class.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/detail/class.h: Python C API implementation details for py::class_
 
@@ -374,6 +373,7 @@ extern "C" inline void pybind11_object_dealloc(PyObject* self)
   auto type = Py_TYPE(self);
   type->tp_free(self);
 
+#if PY_VERSION_HEX < 0x03080000
   // `type->tp_dealloc != pybind11_object_dealloc` means that we're being called
   // as part of a derived type's dealloc, in which case we're not allowed to decref
   // the type here. For cross-module compatibility, we shouldn't compare directly
@@ -381,6 +381,11 @@ extern "C" inline void pybind11_object_dealloc(PyObject* self)
   auto pybind11_object_type = (PyTypeObject*)get_internals().instance_base;
   if (type->tp_dealloc == pybind11_object_type->tp_dealloc)
     Py_DECREF(type);
+#else
+  // This was not needed before Python 3.8 (Python issue 35810)
+  // https://github.com/pybind/pybind11/issues/1946
+  Py_DECREF(type);
+#endif
 }
 
 /** Create the type which can be used as a common base for all classes.  This is
@@ -515,6 +520,13 @@ extern "C" inline int pybind11_getbuffer(PyObject* obj, Py_buffer* view, int fla
   view->len = view->itemsize;
   for (auto s : info->shape)
     view->len *= s;
+  view->readonly = info->readonly;
+  if ((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE && info->readonly) {
+    if (view)
+      view->obj = nullptr;
+    PyErr_SetString(PyExc_BufferError, "Writable buffer requested for readonly storage");
+    return -1;
+  }
   if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
     view->format = const_cast<char*>(info->format.c_str());
   if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
@@ -617,6 +629,9 @@ inline PyObject* make_new_python_type(const type_record& rec)
   type->tp_as_number = &heap_type->as_number;
   type->tp_as_sequence = &heap_type->as_sequence;
   type->tp_as_mapping = &heap_type->as_mapping;
+#if PY_VERSION_HEX >= 0x03050000
+  type->tp_as_async = &heap_type->as_async;
+#endif
 
   /* Flags */
   type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
diff --git a/dune/pybindxi/detail/common.h b/dune/pybindxi/detail/common.h
index ed9476d97bb2372026bbbfcad2a5d6211d2c67c5..5a4c8514ef0eb42ace0b1ce8ef2559f8705123d0 100644
--- a/dune/pybindxi/detail/common.h
+++ b/dune/pybindxi/detail/common.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/detail/common.h -- Basic macros
 
@@ -94,8 +93,8 @@
 #endif
 
 #define PYBIND11_VERSION_MAJOR 2
-#define PYBIND11_VERSION_MINOR 3
-#define PYBIND11_VERSION_PATCH dev1
+#define PYBIND11_VERSION_MINOR 5
+#define PYBIND11_VERSION_PATCH 0
 
 /// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
 #if defined(_MSC_VER)
@@ -104,7 +103,7 @@
 #  endif
 #  pragma warning(push)
 #  pragma warning(disable : 4510 4610 4512 4005)
-#  if defined(_DEBUG)
+#  if defined(_DEBUG) && !defined(Py_DEBUG)
 #    define PYBIND11_DEBUG_MARKER
 #    undef _DEBUG
 #  endif
@@ -114,10 +113,9 @@
 #include <frameobject.h>
 #include <pythread.h>
 
-#if defined(_WIN32) && (defined(min) || defined(max))
-#  error Macro clash with min and max -- define NOMINMAX when compiling your program on Windows
-#endif
-
+/* Python #defines overrides on all sorts of core functions, which
+   tends to weak havok in C++ codebases that expect these to work
+   like regular functions (potentially with several overloads) */
 #if defined(isalnum)
 #  undef isalnum
 #  undef isalpha
@@ -128,6 +126,10 @@
 #  undef toupper
 #endif
 
+#if defined(copysign)
+#  undef copysign
+#endif
+
 #if defined(_MSC_VER)
 #  if defined(PYBIND11_DEBUG_MARKER)
 #    define _DEBUG
@@ -169,7 +171,10 @@
 #  define PYBIND11_STR_TYPE ::pybind11::str
 #  define PYBIND11_BOOL_ATTR "__bool__"
 #  define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_bool)
-#  define PYBIND11_PLUGIN_IMPL(name) extern "C" PYBIND11_EXPORT PyObject* PyInit_##name()
+// Providing a separate declaration to make Clang's -Wmissing-prototypes happy
+#  define PYBIND11_PLUGIN_IMPL(name)                                                                                   \
+    extern "C" PYBIND11_EXPORT PyObject* PyInit_##name();                                                              \
+    extern "C" PYBIND11_EXPORT PyObject* PyInit_##name()
 
 #else
 #  define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
@@ -192,8 +197,10 @@
 #  define PYBIND11_STR_TYPE ::pybind11::bytes
 #  define PYBIND11_BOOL_ATTR "__nonzero__"
 #  define PYBIND11_NB_BOOL(ptr) ((ptr)->nb_nonzero)
+// Providing a separate PyInit decl to make Clang's -Wmissing-prototypes happy
 #  define PYBIND11_PLUGIN_IMPL(name)                                                                                   \
     static PyObject* pybind11_init_wrapper();                                                                          \
+    extern "C" PYBIND11_EXPORT void init##name();                                                                      \
     extern "C" PYBIND11_EXPORT void init##name()                                                                       \
     {                                                                                                                  \
       (void)pybind11_init_wrapper();                                                                                   \
@@ -216,6 +223,7 @@ extern "C"
 #define PYBIND11_STRINGIFY(x) #x
 #define PYBIND11_TOSTRING(x) PYBIND11_STRINGIFY(x)
 #define PYBIND11_CONCAT(first, second) first##second
+#define PYBIND11_ENSURE_INTERNALS_READY pybind11::detail::get_internals();
 
 #define PYBIND11_CHECK_PYTHON_VERSION                                                                                  \
   {                                                                                                                    \
@@ -265,6 +273,7 @@ extern "C"
   PYBIND11_PLUGIN_IMPL(name)                                                                                           \
   {                                                                                                                    \
     PYBIND11_CHECK_PYTHON_VERSION                                                                                      \
+    PYBIND11_ENSURE_INTERNALS_READY                                                                                    \
     try {                                                                                                              \
       return pybind11_init();                                                                                          \
     }                                                                                                                  \
@@ -294,6 +303,7 @@ extern "C"
   PYBIND11_PLUGIN_IMPL(name)                                                                                           \
   {                                                                                                                    \
     PYBIND11_CHECK_PYTHON_VERSION                                                                                      \
+    PYBIND11_ENSURE_INTERNALS_READY                                                                                    \
     auto m = pybind11::module(PYBIND11_TOSTRING(name));                                                                \
     try {                                                                                                              \
       PYBIND11_CONCAT(pybind11_init_, name)(m);                                                                        \
@@ -840,6 +850,8 @@ PYBIND11_RUNTIME_EXCEPTION(index_error, PyExc_IndexError)
 PYBIND11_RUNTIME_EXCEPTION(key_error, PyExc_KeyError)
 PYBIND11_RUNTIME_EXCEPTION(value_error, PyExc_ValueError)
 PYBIND11_RUNTIME_EXCEPTION(type_error, PyExc_TypeError)
+PYBIND11_RUNTIME_EXCEPTION(buffer_error, PyExc_BufferError)
+PYBIND11_RUNTIME_EXCEPTION(import_error, PyExc_ImportError)
 PYBIND11_RUNTIME_EXCEPTION(cast_error, PyExc_RuntimeError) /// Thrown when pybind11::cast or handle::call fail due to a
                                                            /// type casting error
 PYBIND11_RUNTIME_EXCEPTION(reference_cast_error, PyExc_RuntimeError) /// Used internally
@@ -922,10 +934,6 @@ struct nodelete
   {}
 };
 
-// overload_cast requires variable templates: C++14
-#if defined(PYBIND11_CPP14)
-#  define PYBIND11_OVERLOAD_CAST 1
-
 NAMESPACE_BEGIN(detail)
 template <typename... Args>
 struct overload_cast_impl
@@ -952,19 +960,23 @@ struct overload_cast_impl
 };
 NAMESPACE_END(detail)
 
+// overload_cast requires variable templates: C++14
+#if defined(PYBIND11_CPP14)
+#  define PYBIND11_OVERLOAD_CAST 1
 /// Syntax sugar for resolving overloaded function pointers:
 ///  - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
 ///  - sweet:   overload_cast<Arg0, Arg1, Arg2>(&Class::func)
 template <typename... Args>
 static constexpr detail::overload_cast_impl<Args...> overload_cast = {};
 // MSVC 2015 only accepts this particular initialization syntax for this variable template.
+#endif
 
 /// Const member function selector for overload_cast
 ///  - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
 ///  - sweet:   overload_cast<Arg>(&Class::func, const_)
 static constexpr auto const_ = std::true_type{};
 
-#else // no overload_cast: providing something that static_assert-fails:
+#if !defined(PYBIND11_CPP14) // no overload_cast: providing something that static_assert-fails:
 template <typename... Args>
 struct overload_cast
 {
diff --git a/dune/pybindxi/detail/descr.h b/dune/pybindxi/detail/descr.h
index e6b65fd0496bc1cbc3edf8f0b53060b5e0acfc1b..630e937a23363366f0e989470d291e220611ddee 100644
--- a/dune/pybindxi/detail/descr.h
+++ b/dune/pybindxi/detail/descr.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/detail/descr.h: Helper type for concatenating type signatures at compile time
 
diff --git a/dune/pybindxi/detail/init.h b/dune/pybindxi/detail/init.h
index b20657d5823ffb55d22990e934542433e60a4550..fda1b48912537159ebd53171b5cf646f1e8ee7e9 100644
--- a/dune/pybindxi/detail/init.h
+++ b/dune/pybindxi/detail/init.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/detail/init.h: init factory function implementation and support code.
 
diff --git a/dune/pybindxi/detail/internals.h b/dune/pybindxi/detail/internals.h
index 7ddaf73f2161c21682d3b5bc004f33022ac527a9..3712da057723bb07358234f5a5e2b8f0a3404d09 100644
--- a/dune/pybindxi/detail/internals.h
+++ b/dune/pybindxi/detail/internals.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/detail/internals.h: Internal data structure and related functions
 
@@ -26,6 +25,7 @@ inline PyObject* make_object_base_type(PyTypeObject* metaclass);
 #  define PYBIND11_TLS_GET_VALUE(key) PyThread_tss_get((key))
 #  define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_tss_set((key), (value))
 #  define PYBIND11_TLS_DELETE_VALUE(key) PyThread_tss_set((key), nullptr)
+#  define PYBIND11_TLS_FREE(key) PyThread_tss_free(key)
 #else
 // Usually an int but a long on Cygwin64 with Python 3.x
 #  define PYBIND11_TLS_KEY_INIT(var) decltype(PyThread_create_key()) var = 0
@@ -41,6 +41,7 @@ inline PyObject* make_object_base_type(PyTypeObject* metaclass);
 #    define PYBIND11_TLS_DELETE_VALUE(key) PyThread_set_key_value((key), nullptr)
 #    define PYBIND11_TLS_REPLACE_VALUE(key, value) PyThread_set_key_value((key), (value))
 #  endif
+#  define PYBIND11_TLS_FREE(key) (void)key
 #endif
 
 // Python loads modules by default with dlopen with the RTLD_LOCAL flag; under libc++ and possibly
@@ -117,6 +118,17 @@ struct internals
 #if defined(WITH_THREAD)
   PYBIND11_TLS_KEY_INIT(tstate);
   PyInterpreterState* istate = nullptr;
+  ~internals()
+  {
+    // This destructor is called *after* Py_Finalize() in finalize_interpreter().
+    // That *SHOULD BE* fine. The following details what happens whe PyThread_tss_free is called.
+    // PYBIND11_TLS_FREE is PyThread_tss_free on python 3.7+. On older python, it does nothing.
+    // PyThread_tss_free calls PyThread_tss_delete and PyMem_RawFree.
+    // PyThread_tss_delete just calls TlsFree (on Windows) or pthread_key_delete (on *NIX). Neither
+    // of those have anything to do with CPython internals.
+    // PyMem_RawFree *requires* that the `tstate` be allocated with the CPython allocator.
+    PYBIND11_TLS_FREE(tstate);
+  }
 #endif
 };
 
@@ -148,14 +160,49 @@ struct type_info
 };
 
 /// Tracks the `internals` and `type_info` ABI version independent of the main library version
-#define PYBIND11_INTERNALS_VERSION 3
+#define PYBIND11_INTERNALS_VERSION 4
 
-#if defined(_DEBUG)
+/// On MSVC, debug and release builds are not ABI-compatible!
+#if defined(_MSC_VER) && defined(_DEBUG)
 #  define PYBIND11_BUILD_TYPE "_debug"
 #else
 #  define PYBIND11_BUILD_TYPE ""
 #endif
 
+/// Let's assume that different compilers are ABI-incompatible.
+#if defined(_MSC_VER)
+#  define PYBIND11_COMPILER_TYPE "_msvc"
+#elif defined(__INTEL_COMPILER)
+#  define PYBIND11_COMPILER_TYPE "_icc"
+#elif defined(__clang__)
+#  define PYBIND11_COMPILER_TYPE "_clang"
+#elif defined(__PGI)
+#  define PYBIND11_COMPILER_TYPE "_pgi"
+#elif defined(__MINGW32__)
+#  define PYBIND11_COMPILER_TYPE "_mingw"
+#elif defined(__CYGWIN__)
+#  define PYBIND11_COMPILER_TYPE "_gcc_cygwin"
+#elif defined(__GNUC__)
+#  define PYBIND11_COMPILER_TYPE "_gcc"
+#else
+#  define PYBIND11_COMPILER_TYPE "_unknown"
+#endif
+
+#if defined(_LIBCPP_VERSION)
+#  define PYBIND11_STDLIB "_libcpp"
+#elif defined(__GLIBCXX__) || defined(__GLIBCPP__)
+#  define PYBIND11_STDLIB "_libstdcpp"
+#else
+#  define PYBIND11_STDLIB ""
+#endif
+
+/// On Linux/OSX, changes in __GXX_ABI_VERSION__ indicate ABI incompatibility.
+#if defined(__GXX_ABI_VERSION)
+#  define PYBIND11_BUILD_ABI "_cxxabi" PYBIND11_TOSTRING(__GXX_ABI_VERSION)
+#else
+#  define PYBIND11_BUILD_ABI ""
+#endif
+
 #if defined(WITH_THREAD)
 #  define PYBIND11_INTERNALS_KIND ""
 #else
@@ -163,12 +210,12 @@ struct type_info
 #endif
 
 #define PYBIND11_INTERNALS_ID                                                                                          \
-  "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION) PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE   \
-      "__"
+  "__pybind11_internals_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION)                                               \
+      PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
 
 #define PYBIND11_MODULE_LOCAL_ID                                                                                       \
   "__pybind11_module_local_v" PYBIND11_TOSTRING(PYBIND11_INTERNALS_VERSION)                                            \
-      PYBIND11_INTERNALS_KIND PYBIND11_BUILD_TYPE "__"
+      PYBIND11_INTERNALS_KIND PYBIND11_COMPILER_TYPE PYBIND11_STDLIB PYBIND11_BUILD_ABI PYBIND11_BUILD_TYPE "__"
 
 /// Each module locally stores a pointer to the `internals` data. The data
 /// itself is shared among modules with the same `PYBIND11_INTERNALS_ID`.
@@ -178,6 +225,63 @@ inline internals**& get_internals_pp()
   return internals_pp;
 }
 
+inline void translate_exception(std::exception_ptr p)
+{
+  try {
+    if (p)
+      std::rethrow_exception(p);
+  } catch (error_already_set& e) {
+    e.restore();
+    return;
+  } catch (const builtin_exception& e) {
+    e.set_error();
+    return;
+  } catch (const std::bad_alloc& e) {
+    PyErr_SetString(PyExc_MemoryError, e.what());
+    return;
+  } catch (const std::domain_error& e) {
+    PyErr_SetString(PyExc_ValueError, e.what());
+    return;
+  } catch (const std::invalid_argument& e) {
+    PyErr_SetString(PyExc_ValueError, e.what());
+    return;
+  } catch (const std::length_error& e) {
+    PyErr_SetString(PyExc_ValueError, e.what());
+    return;
+  } catch (const std::out_of_range& e) {
+    PyErr_SetString(PyExc_IndexError, e.what());
+    return;
+  } catch (const std::range_error& e) {
+    PyErr_SetString(PyExc_ValueError, e.what());
+    return;
+  } catch (const std::overflow_error& e) {
+    PyErr_SetString(PyExc_OverflowError, e.what());
+    return;
+  } catch (const std::exception& e) {
+    PyErr_SetString(PyExc_RuntimeError, e.what());
+    return;
+  } catch (...) {
+    PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
+    return;
+  }
+}
+
+#if !defined(__GLIBCXX__)
+inline void translate_local_exception(std::exception_ptr p)
+{
+  try {
+    if (p)
+      std::rethrow_exception(p);
+  } catch (error_already_set& e) {
+    e.restore();
+    return;
+  } catch (const builtin_exception& e) {
+    e.set_error();
+    return;
+  }
+}
+#endif
+
 /// Return a reference to the current `internals` data
 PYBIND11_NOINLINE inline internals& get_internals()
 {
@@ -185,6 +289,20 @@ PYBIND11_NOINLINE inline internals& get_internals()
   if (internals_pp && *internals_pp)
     return **internals_pp;
 
+  // Ensure that the GIL is held since we will need to make Python calls.
+  // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals.
+  struct gil_scoped_acquire_local
+  {
+    gil_scoped_acquire_local()
+      : state(PyGILState_Ensure())
+    {}
+    ~gil_scoped_acquire_local()
+    {
+      PyGILState_Release(state);
+    }
+    const PyGILState_STATE state;
+  } gil;
+
   constexpr auto* id = PYBIND11_INTERNALS_ID;
   auto builtins = handle(PyEval_GetBuiltins());
   if (builtins.contains(id) && isinstance<capsule>(builtins[id])) {
@@ -196,18 +314,7 @@ PYBIND11_NOINLINE inline internals& get_internals()
     //
     // libstdc++ doesn't require this (types there are identified only by name)
 #if !defined(__GLIBCXX__)
-    (*internals_pp)->registered_exception_translators.push_front([](std::exception_ptr p) -> void {
-      try {
-        if (p)
-          std::rethrow_exception(p);
-      } catch (error_already_set& e) {
-        e.restore();
-        return;
-      } catch (const builtin_exception& e) {
-        e.set_error();
-        return;
-      }
-    });
+    (*internals_pp)->registered_exception_translators.push_front(&translate_local_exception);
 #endif
   } else {
     if (!internals_pp)
@@ -231,42 +338,7 @@ PYBIND11_NOINLINE inline internals& get_internals()
     internals_ptr->istate = tstate->interp;
 #endif
     builtins[id] = capsule(internals_pp);
-    internals_ptr->registered_exception_translators.push_front([](std::exception_ptr p) -> void {
-      try {
-        if (p)
-          std::rethrow_exception(p);
-      } catch (error_already_set& e) {
-        e.restore();
-        return;
-      } catch (const builtin_exception& e) {
-        e.set_error();
-        return;
-      } catch (const std::bad_alloc& e) {
-        PyErr_SetString(PyExc_MemoryError, e.what());
-        return;
-      } catch (const std::domain_error& e) {
-        PyErr_SetString(PyExc_ValueError, e.what());
-        return;
-      } catch (const std::invalid_argument& e) {
-        PyErr_SetString(PyExc_ValueError, e.what());
-        return;
-      } catch (const std::length_error& e) {
-        PyErr_SetString(PyExc_ValueError, e.what());
-        return;
-      } catch (const std::out_of_range& e) {
-        PyErr_SetString(PyExc_IndexError, e.what());
-        return;
-      } catch (const std::range_error& e) {
-        PyErr_SetString(PyExc_ValueError, e.what());
-        return;
-      } catch (const std::exception& e) {
-        PyErr_SetString(PyExc_RuntimeError, e.what());
-        return;
-      } catch (...) {
-        PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
-        return;
-      }
-    });
+    internals_ptr->registered_exception_translators.push_front(&translate_exception);
     internals_ptr->static_property_type = make_static_property_type();
     internals_ptr->default_metaclass = make_default_metaclass();
     internals_ptr->instance_base = make_object_base_type(internals_ptr->default_metaclass);
diff --git a/dune/pybindxi/detail/typeid.h b/dune/pybindxi/detail/typeid.h
index f2d5d631111810216c241d7c6a7c3da372fe2d94..010d0e22c8011b88ae2bfded22e875af878ce31a 100644
--- a/dune/pybindxi/detail/typeid.h
+++ b/dune/pybindxi/detail/typeid.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/detail/typeid.h: Compiler-independent access to type identifiers
 
diff --git a/dune/pybindxi/eigen.h b/dune/pybindxi/eigen.h
index 56ecd9797faa0ea0e83023e079a369a4b1ae32d1..78066c629274d4e2380d40c2d61aae096715386a 100644
--- a/dune/pybindxi/eigen.h
+++ b/dune/pybindxi/eigen.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/eigen.h: Transparent conversion for dense and sparse Eigen matrices
 
diff --git a/dune/pybindxi/embed.h b/dune/pybindxi/embed.h
index 0aedd584701998d4fd913d9a519555fbaed35f74..5a84e0da213d443d4d62a758775c3b7c119415d8 100644
--- a/dune/pybindxi/embed.h
+++ b/dune/pybindxi/embed.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/embed.h: Support for embedding the interpreter
 
@@ -19,12 +18,14 @@
 
 #if PY_MAJOR_VERSION >= 3
 #  define PYBIND11_EMBEDDED_MODULE_IMPL(name)                                                                          \
+    extern "C" PyObject* pybind11_init_impl_##name();                                                                  \
     extern "C" PyObject* pybind11_init_impl_##name()                                                                   \
     {                                                                                                                  \
       return pybind11_init_wrapper_##name();                                                                           \
     }
 #else
 #  define PYBIND11_EMBEDDED_MODULE_IMPL(name)                                                                          \
+    extern "C" void pybind11_init_impl_##name();                                                                       \
     extern "C" void pybind11_init_impl_##name()                                                                        \
     {                                                                                                                  \
       pybind11_init_wrapper_##name();                                                                                  \
diff --git a/dune/pybindxi/eval.h b/dune/pybindxi/eval.h
index 6cf02abb8bf926409f62e5e9c5a8d01b05347eca..e58f6c7af1adee0ca54acda452c7336d8929acc0 100644
--- a/dune/pybindxi/eval.h
+++ b/dune/pybindxi/eval.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/exec.h: Support for evaluating Python expressions and statements
     from strings and files
diff --git a/dune/pybindxi/functional.h b/dune/pybindxi/functional.h
index 6e515584529cd3c43e308c5533567469e2191150..54e8199e5938882d089d3eda41ce2b5c2b00af6a 100644
--- a/dune/pybindxi/functional.h
+++ b/dune/pybindxi/functional.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/functional.h: std::function<> support
 
@@ -76,12 +75,23 @@ public:
       }
     };
 
-    value = [hfunc = func_handle(std::move(func))](Args... args) -> Return {
-      gil_scoped_acquire acq;
-      object retval(hfunc.f(std::forward<Args>(args)...));
-      /* Visual studio 2015 parser issue: need parentheses around this expression */
-      return (retval.template cast<Return>());
+    // to emulate 'move initialization capture' in C++11
+    struct func_wrapper
+    {
+      func_handle hfunc;
+      func_wrapper(func_handle&& hf)
+        : hfunc(std::move(hf))
+      {}
+      Return operator()(Args... args) const
+      {
+        gil_scoped_acquire acq;
+        object retval(hfunc.f(std::forward<Args>(args)...));
+        /* Visual studio 2015 parser issue: need parentheses around this expression */
+        return (retval.template cast<Return>());
+      }
     };
+
+    value = func_wrapper(func_handle(std::move(func)));
     return true;
   }
 
diff --git a/dune/pybindxi/iostream.h b/dune/pybindxi/iostream.h
index 60c98dce87e12d5b56a8e1589943a7728ae7205e..f393629b912633bc434e1fc51965068f5514452e 100644
--- a/dune/pybindxi/iostream.h
+++ b/dune/pybindxi/iostream.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python
 
@@ -68,6 +67,8 @@ public:
     setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
   }
 
+  pythonbuf(pythonbuf&&) = default;
+
   /// Sync before destroy
   ~pythonbuf()
   {
diff --git a/dune/pybindxi/numpy.h b/dune/pybindxi/numpy.h
index 042101e52495f8a7f0a0c6351ba2a2cdb916a69d..284abbbbe169dc2fc881b2dcf3956f520aa8a082 100644
--- a/dune/pybindxi/numpy.h
+++ b/dune/pybindxi/numpy.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/numpy.h: Basic NumPy support, vectorize() wrapper
 
@@ -15,6 +14,7 @@
 #include <numeric>
 #include <algorithm>
 #include <array>
+#include <cstdint>
 #include <cstdlib>
 #include <cstring>
 #include <sstream>
@@ -117,6 +117,26 @@ inline numpy_internals& get_numpy_internals()
   return *ptr;
 }
 
+template <typename T>
+struct same_size
+{
+  template <typename U>
+  using as = bool_constant<sizeof(T) == sizeof(U)>;
+};
+
+template <typename Concrete>
+constexpr int platform_lookup()
+{
+  return -1;
+}
+
+// Lookup a type according to its size, and return a value corresponding to the NumPy typenum.
+template <typename Concrete, typename T, typename... Ts, typename... Ints>
+constexpr int platform_lookup(int I, Ints... Is)
+{
+  return sizeof(Concrete) == sizeof(T) ? I : platform_lookup<Concrete, Ts...>(Is...);
+}
+
 struct npy_api
 {
   enum constants
@@ -148,7 +168,21 @@ struct npy_api
     NPY_OBJECT_ = 17,
     NPY_STRING_,
     NPY_UNICODE_,
-    NPY_VOID_
+    NPY_VOID_,
+    // Platform-dependent normalization
+    NPY_INT8_ = NPY_BYTE_,
+    NPY_UINT8_ = NPY_UBYTE_,
+    NPY_INT16_ = NPY_SHORT_,
+    NPY_UINT16_ = NPY_USHORT_,
+    // `npy_common.h` defines the integer aliases. In order, it checks:
+    // NPY_BITSOF_LONG, NPY_BITSOF_LONGLONG, NPY_BITSOF_INT, NPY_BITSOF_SHORT, NPY_BITSOF_CHAR
+    // and assigns the alias to the first matching size, so we should check in this order.
+    NPY_INT32_ = platform_lookup<std::int32_t, long, int, short>(NPY_LONG_, NPY_INT_, NPY_SHORT_),
+    NPY_UINT32_ =
+        platform_lookup<std::uint32_t, unsigned long, unsigned int, unsigned short>(NPY_ULONG_, NPY_UINT_, NPY_USHORT_),
+    NPY_INT64_ = platform_lookup<std::int64_t, long, long long, int>(NPY_LONG_, NPY_LONGLONG_, NPY_INT_),
+    NPY_UINT64_ = platform_lookup<std::uint64_t, unsigned long, unsigned long long, unsigned int>(
+        NPY_ULONG_, NPY_ULONGLONG_, NPY_UINT_),
   };
 
   typedef struct
@@ -1241,12 +1275,12 @@ private:
   constexpr static const int values[15] = {npy_api::NPY_BOOL_,
                                            npy_api::NPY_BYTE_,
                                            npy_api::NPY_UBYTE_,
-                                           npy_api::NPY_SHORT_,
-                                           npy_api::NPY_USHORT_,
-                                           npy_api::NPY_INT_,
-                                           npy_api::NPY_UINT_,
-                                           npy_api::NPY_LONGLONG_,
-                                           npy_api::NPY_ULONGLONG_,
+                                           npy_api::NPY_INT16_,
+                                           npy_api::NPY_UINT16_,
+                                           npy_api::NPY_INT32_,
+                                           npy_api::NPY_UINT32_,
+                                           npy_api::NPY_INT64_,
+                                           npy_api::NPY_UINT64_,
                                            npy_api::NPY_FLOAT_,
                                            npy_api::NPY_DOUBLE_,
                                            npy_api::NPY_LONGDOUBLE_,
@@ -1260,7 +1294,7 @@ public:
   static pybind11::dtype dtype()
   {
     if (auto ptr = npy_api::get().PyArray_DescrFromType_(value))
-      return reinterpret_borrow<pybind11::dtype>(ptr);
+      return reinterpret_steal<pybind11::dtype>(ptr);
     pybind11_fail("Unsupported buffer format!");
   }
 };
@@ -1334,8 +1368,15 @@ inline PYBIND11_NOINLINE void register_structured_dtype(any_container<field_desc
   if (numpy_internals.get_type_info(tinfo, false))
     pybind11_fail("NumPy: dtype is already registered");
 
+  // Use ordered fields because order matters as of NumPy 1.14:
+  // https://docs.scipy.org/doc/numpy/release.html#multiple-field-indexing-assignment-of-structured-arrays
+  std::vector<field_descriptor> ordered_fields(std::move(fields));
+  std::sort(ordered_fields.begin(), ordered_fields.end(), [](const field_descriptor& a, const field_descriptor& b) {
+    return a.offset < b.offset;
+  });
+
   list names, formats, offsets;
-  for (auto field : *fields) {
+  for (auto& field : ordered_fields) {
     if (!field.descr)
       pybind11_fail(std::string("NumPy: unsupported field dtype: `") + field.name + "` @ " + tinfo.name());
     names.append(PYBIND11_STR_TYPE(field.name));
@@ -1351,10 +1392,6 @@ inline PYBIND11_NOINLINE void register_structured_dtype(any_container<field_desc
   // - https://github.com/numpy/numpy/pull/7798
   // Because of this, we won't use numpy's logic to generate buffer format
   // strings and will just do it ourselves.
-  std::vector<field_descriptor> ordered_fields(std::move(fields));
-  std::sort(ordered_fields.begin(), ordered_fields.end(), [](const field_descriptor& a, const field_descriptor& b) {
-    return a.offset < b.offset;
-  });
   ssize_t offset = 0;
   std::ostringstream oss;
   // mark the structure as unaligned with '^', because numpy and C++ don't
diff --git a/dune/pybindxi/operators.h b/dune/pybindxi/operators.h
index b226e5530cdd23e35637469778268afa6364de16..232cc332323427a27b25f9f590b0591b3d6514e8 100644
--- a/dune/pybindxi/operators.h
+++ b/dune/pybindxi/operators.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/operator.h: Metatemplates for operator overloading
 
diff --git a/dune/pybindxi/options.h b/dune/pybindxi/options.h
index 68b19364e17bf52681bc4f1d5540e1dfcf82d275..055d6dfc03d9a834fe507148ecc0bd8fc1e1183f 100644
--- a/dune/pybindxi/options.h
+++ b/dune/pybindxi/options.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/options.h: global settings that are configurable at runtime.
 
diff --git a/dune/pybindxi/pybind11.h b/dune/pybindxi/pybind11.h
index 119899ed05c18ee47c9472f10797454cdeabc91a..820490c8274edc7b82c031b42b7dde4a3b2eabbc 100644
--- a/dune/pybindxi/pybind11.h
+++ b/dune/pybindxi/pybind11.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/pybind11.h: Main header file of the C++11 python
     binding generator library
@@ -521,7 +520,7 @@ protected:
 
         function_call call(func, parent);
 
-        size_t args_to_copy = std::min(pos_args, n_args_in);
+        size_t args_to_copy = (std::min)(pos_args, n_args_in); // Protect std::min with parentheses
         size_t args_copied = 0;
 
         // 0. Inject new-style `self` argument
@@ -1061,11 +1060,18 @@ inline void call_operator_delete(void* p, size_t s, size_t a)
 {
   (void)s;
   (void)a;
-#if defined(PYBIND11_CPP17)
-  if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+#if defined(__cpp_aligned_new) && (!defined(_MSC_VER) || _MSC_VER >= 1912)
+  if (a > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+#  ifdef __cpp_sized_deallocation
     ::operator delete(p, s, std::align_val_t(a));
-  else
-    ::operator delete(p, s);
+#  else
+    ::operator delete(p, std::align_val_t(a));
+#  endif
+    return;
+  }
+#endif
+#ifdef __cpp_sized_deallocation
+  ::operator delete(p, s);
 #else
   ::operator delete(p);
 #endif
@@ -1602,9 +1608,17 @@ struct enum_base
       },                                                                                                               \
       is_method(m_base))
 
+#define PYBIND11_ENUM_OP_CONV_LHS(op, expr)                                                                            \
+  m_base.attr(op) = cpp_function(                                                                                      \
+      [](object a_, object b) {                                                                                        \
+        int_ a(a_);                                                                                                    \
+        return expr;                                                                                                   \
+      },                                                                                                               \
+      is_method(m_base))
+
     if (is_convertible) {
-      PYBIND11_ENUM_OP_CONV("__eq__", !b.is_none() && a.equal(b));
-      PYBIND11_ENUM_OP_CONV("__ne__", b.is_none() || !a.equal(b));
+      PYBIND11_ENUM_OP_CONV_LHS("__eq__", !b.is_none() && a.equal(b));
+      PYBIND11_ENUM_OP_CONV_LHS("__ne__", b.is_none() || !a.equal(b));
 
       if (is_arithmetic) {
         PYBIND11_ENUM_OP_CONV("__lt__", a < b);
@@ -1617,6 +1631,7 @@ struct enum_base
         PYBIND11_ENUM_OP_CONV("__ror__", a | b);
         PYBIND11_ENUM_OP_CONV("__xor__", a ^ b);
         PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b);
+        m_base.attr("__invert__") = cpp_function([](object arg) { return ~(int_(arg)); }, is_method(m_base));
       }
     } else {
       PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false);
@@ -1632,6 +1647,7 @@ struct enum_base
       }
     }
 
+#undef PYBIND11_ENUM_OP_CONV_LHS
 #undef PYBIND11_ENUM_OP_CONV
 #undef PYBIND11_ENUM_OP_STRICT
 
@@ -1693,6 +1709,10 @@ public:
 #if PY_MAJOR_VERSION < 3
     def("__long__", [](Type value) { return (Scalar)value; });
 #endif
+#if PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8)
+    def("__index__", [](Type value) { return (Scalar)value; });
+#endif
+
     cpp_function setstate([](Type& value, Scalar arg) { value = static_cast<Type>(arg); }, is_method(*this));
     attr("__setstate__") = setstate;
   }
@@ -2205,8 +2225,8 @@ class gil_scoped_release
 error_already_set::~error_already_set()
 {
   if (m_type) {
-    error_scope scope;
     gil_scoped_acquire gil;
+    error_scope scope;
     m_type.release().dec_ref();
     m_value.release().dec_ref();
     m_trace.release().dec_ref();
diff --git a/dune/pybindxi/pytypes.h b/dune/pybindxi/pytypes.h
index 65bd1d1faa2e8631d4aa243d6e464a5d8672af29..9d52eff494a360459af077bd9d80abd8c38e81a6 100644
--- a/dune/pybindxi/pytypes.h
+++ b/dune/pybindxi/pytypes.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/pytypes.h: Convenience wrapper classes for basic Python types
 
@@ -1733,6 +1732,10 @@ public:
   {
     return (size_t)PyTuple_Size(m_ptr);
   }
+  bool empty() const
+  {
+    return size() == 0;
+  }
   detail::tuple_accessor operator[](size_t index) const
   {
     return {*this, index};
@@ -1773,6 +1776,10 @@ public:
   {
     return (size_t)PyDict_Size(m_ptr);
   }
+  bool empty() const
+  {
+    return size() == 0;
+  }
   detail::dict_iterator begin() const
   {
     return {*this, 0};
@@ -1785,13 +1792,10 @@ public:
   {
     PyDict_Clear(ptr());
   }
-  bool contains(handle key) const
-  {
-    return PyDict_Contains(ptr(), key.ptr()) == 1;
-  }
-  bool contains(const char* key) const
+  template <typename T>
+  bool contains(T&& key) const
   {
-    return PyDict_Contains(ptr(), pybind11::str(key).ptr()) == 1;
+    return PyDict_Contains(m_ptr, detail::object_or_cast(std::forward<T>(key)).ptr()) == 1;
   }
 
 private:
@@ -1812,6 +1816,10 @@ public:
   {
     return (size_t)PySequence_Size(m_ptr);
   }
+  bool empty() const
+  {
+    return size() == 0;
+  }
   detail::sequence_accessor operator[](size_t index) const
   {
     return {*this, index};
@@ -1844,6 +1852,10 @@ public:
   {
     return (size_t)PyList_Size(m_ptr);
   }
+  bool empty() const
+  {
+    return size() == 0;
+  }
   detail::list_accessor operator[](size_t index) const
   {
     return {*this, index};
@@ -1865,6 +1877,11 @@ public:
   {
     PyList_Append(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr());
   }
+  template <typename T>
+  void insert(size_t index, T&& val) const
+  {
+    PyList_Insert(m_ptr, static_cast<ssize_t>(index), detail::object_or_cast(std::forward<T>(val)).ptr());
+  }
 };
 
 class args : public tuple
@@ -1890,6 +1907,10 @@ public:
   {
     return (size_t)PySet_Size(m_ptr);
   }
+  bool empty() const
+  {
+    return size() == 0;
+  }
   template <typename T>
   bool add(T&& val) const
   {
@@ -1899,6 +1920,11 @@ public:
   {
     PySet_Clear(m_ptr);
   }
+  template <typename T>
+  bool contains(T&& val) const
+  {
+    return PySet_Contains(m_ptr, detail::object_or_cast(std::forward<T>(val)).ptr()) == 1;
+  }
 };
 
 class function : public object
@@ -1929,7 +1955,7 @@ class buffer : public object
 public:
   PYBIND11_OBJECT_DEFAULT(buffer, object, PyObject_CheckBuffer)
 
-  buffer_info request(bool writable = false)
+  buffer_info request(bool writable = false) const
   {
     int flags = PyBUF_STRIDES | PyBUF_FORMAT;
     if (writable)
@@ -1966,7 +1992,7 @@ public:
     buf.strides = py_strides.data();
     buf.shape = py_shape.data();
     buf.suboffsets = nullptr;
-    buf.readonly = false;
+    buf.readonly = info.readonly;
     buf.internal = nullptr;
 
     m_ptr = PyMemoryView_FromBuffer(&buf);
diff --git a/dune/pybindxi/stl.h b/dune/pybindxi/stl.h
index 9272e6daf57b863a4873de6bddd0eb4edb16a748..a11e756447b91c6dadfd3e7e5e04fda5ee4a1ea9 100644
--- a/dune/pybindxi/stl.h
+++ b/dune/pybindxi/stl.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/stl.h: Transparent conversion for STL data types
 
diff --git a/dune/pybindxi/stl_bind.h b/dune/pybindxi/stl_bind.h
index 9d48d19c6f8acf98744a40dd134c9cfe5ce57c2c..f200de62e6bafcee1de7eee761a162b1d220085f 100644
--- a/dune/pybindxi/stl_bind.h
+++ b/dune/pybindxi/stl_bind.h
@@ -1,4 +1,3 @@
-#pragma GCC system_header
 /*
     pybind11/std_bind.h: Binding generators for STL data types
 
@@ -133,6 +132,14 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
   using SizeType = typename Vector::size_type;
   using DiffType = typename Vector::difference_type;
 
+  auto wrap_i = [](DiffType i, SizeType n) {
+    if (i < 0)
+      i += n;
+    if (i < 0 || (SizeType)i >= n)
+      throw index_error();
+    return i;
+  };
+
   cl.def(
       "append", [](Vector& v, const T& value) { v.push_back(value); }, arg("x"), "Add an item to the end of the list");
 
@@ -144,6 +151,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
     return v.release();
   }));
 
+  cl.def(
+      "clear", [](Vector& v) { v.clear(); }, "Clear the contents");
+
   cl.def(
       "extend",
       [](Vector& v, const Vector& src) { v.insert(v.end(), src.begin(), src.end()); },
@@ -174,10 +184,13 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
 
   cl.def(
       "insert",
-      [](Vector& v, SizeType i, const T& x) {
-        if (i > v.size())
+      [](Vector& v, DiffType i, const T& x) {
+        // Can't use wrap_i; i == v.size() is OK
+        if (i < 0)
+          i += v.size();
+        if (i < 0 || (SizeType)i > v.size())
           throw index_error();
-        v.insert(v.begin() + (DiffType)i, x);
+        v.insert(v.begin() + i, x);
       },
       arg("i"),
       arg("x"),
@@ -196,20 +209,18 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
 
   cl.def(
       "pop",
-      [](Vector& v, SizeType i) {
-        if (i >= v.size())
-          throw index_error();
-        T t = v[i];
-        v.erase(v.begin() + (DiffType)i);
+      [wrap_i](Vector& v, DiffType i) {
+        i = wrap_i(i, v.size());
+        T t = v[(SizeType)i];
+        v.erase(v.begin() + i);
         return t;
       },
       arg("i"),
       "Remove and return the item at index ``i``");
 
-  cl.def("__setitem__", [](Vector& v, SizeType i, const T& t) {
-    if (i >= v.size())
-      throw index_error();
-    v[i] = t;
+  cl.def("__setitem__", [wrap_i](Vector& v, DiffType i, const T& t) {
+    i = wrap_i(i, v.size());
+    v[(SizeType)i] = t;
   });
 
   /// Slicing protocol
@@ -252,10 +263,9 @@ void vector_modifiers(enable_if_t<is_copy_constructible<typename Vector::value_t
 
   cl.def(
       "__delitem__",
-      [](Vector& v, SizeType i) {
-        if (i >= v.size())
-          throw index_error();
-        v.erase(v.begin() + DiffType(i));
+      [wrap_i](Vector& v, DiffType i) {
+        i = wrap_i(i, v.size());
+        v.erase(v.begin() + i);
       },
       "Delete the list elements at index ``i``");
 
@@ -291,14 +301,22 @@ void vector_accessor(enable_if_t<!vector_needs_copy<Vector>::value, Class_>& cl)
 {
   using T = typename Vector::value_type;
   using SizeType = typename Vector::size_type;
+  using DiffType = typename Vector::difference_type;
   using ItType = typename Vector::iterator;
 
+  auto wrap_i = [](DiffType i, SizeType n) {
+    if (i < 0)
+      i += n;
+    if (i < 0 || (SizeType)i >= n)
+      throw index_error();
+    return i;
+  };
+
   cl.def(
       "__getitem__",
-      [](Vector& v, SizeType i) -> T& {
-        if (i >= v.size())
-          throw index_error();
-        return v[i];
+      [wrap_i](Vector& v, DiffType i) -> T& {
+        i = wrap_i(i, v.size());
+        return v[(SizeType)i];
       },
       return_value_policy::reference_internal // ref + keepalive
   );
@@ -318,11 +336,14 @@ void vector_accessor(enable_if_t<vector_needs_copy<Vector>::value, Class_>& cl)
 {
   using T = typename Vector::value_type;
   using SizeType = typename Vector::size_type;
+  using DiffType = typename Vector::difference_type;
   using ItType = typename Vector::iterator;
-  cl.def("__getitem__", [](const Vector& v, SizeType i) -> T {
-    if (i >= v.size())
+  cl.def("__getitem__", [](const Vector& v, DiffType i) -> T {
+    if (i < 0 && (i += v.size()) < 0)
+      throw index_error();
+    if ((SizeType)i >= v.size())
       throw index_error();
-    return v[i];
+    return v[(SizeType)i];
   });
 
   cl.def(
@@ -514,7 +535,7 @@ void map_assignment(const Args&...)
 
 // Map assignment when copy-assignable: just copy the value
 template <typename Map, typename Class_>
-void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_type>::value, Class_>& cl)
+void map_assignment(enable_if_t<is_copy_assignable<typename Map::mapped_type>::value, Class_>& cl)
 {
   using KeyType = typename Map::key_type;
   using MappedType = typename Map::mapped_type;
@@ -530,7 +551,7 @@ void map_assignment(enable_if_t<std::is_copy_assignable<typename Map::mapped_typ
 
 // Not copy-assignable, but still copy-constructible: we can update the value by erasing and reinserting
 template <typename Map, typename Class_>
-void map_assignment(enable_if_t<!std::is_copy_assignable<typename Map::mapped_type>::value
+void map_assignment(enable_if_t<!is_copy_assignable<typename Map::mapped_type>::value
                                     && is_copy_constructible<typename Map::mapped_type>::value,
                                 Class_>& cl)
 {