diff --git a/PyTools/Embed/PyObjectPtr.cpp b/PyTools/Embed/PyObjectPtr.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..55390c99ab52ed8a8d7932c86c3661e78e1f6387
--- /dev/null
+++ b/PyTools/Embed/PyObjectPtr.cpp
@@ -0,0 +1,83 @@
+#include "PyObjectPtr.h"
+#include "PythonInterpreter.h"
+
+#include <string>
+#include <sstream>  // stringstream
+#include <exception>
+
+PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr,
+                         const Status& status):
+    ptr{pyobject_ptr},
+    status{status}
+{
+    // if PyObject pointer is null but result type is not `None` or `Error`,
+    // then that will be an `Error` case.
+    if(!ptr && status.type != Status::Type::None
+       && status.type != Status::Type::Error)
+    {
+        this->status.type = Status::Type::Error;
+    }
+}
+
+
+PyObjectPtr::PyObjectPtr(PyObject* pyobject_ptr):
+    PyObjectPtr(pyobject_ptr, Status(Status::Type::Info)) {}
+
+
+PyObjectPtr::PyObjectPtr(const Status& status):
+    PyObjectPtr(nullptr, status) {}
+
+
+PyObjectPtr::~PyObjectPtr()
+{
+    discard();
+}
+
+
+PyObjectPtr::PyObjectPtr(PyObjectPtr&& other):
+    PyObjectPtr(other.ptr, other.status)
+{
+    // reset the moved object
+    other.reset();
+}
+
+
+PyObject* PyObjectPtr::release()
+{
+    PyObject* pyobject_ptr {ptr};
+    reset();
+    return pyobject_ptr;
+}
+
+
+void PyObjectPtr::reset()
+{
+    ptr = nullptr;
+    status = Status();
+}
+
+
+void PyObjectPtr::discard()
+{
+    if (!PythonInterpreter::isInitialized()) {
+        throw(std::runtime_error(
+                  "Decrementing Python reference-count without "
+                  "Python initialized leads to memory access violation "
+                  "(segmentation fault)"));
+    }
+
+    PythonInterpreter::DecRef(ptr);
+    reset();
+}
+
+
+bool PyObjectPtr::valid() const
+{
+    return (ptr && (status.isOK() || status.isWarning()));
+}
+
+
+bool PyObjectPtr::isSet() const
+{
+    return (ptr && status.isSet());
+}
diff --git a/PyTools/Embed/PyObjectPtr.h b/PyTools/Embed/PyObjectPtr.h
new file mode 100644
index 0000000000000000000000000000000000000000..2165b64770a5a49be936607051778204e627cd3e
--- /dev/null
+++ b/PyTools/Embed/PyObjectPtr.h
@@ -0,0 +1,72 @@
+#ifndef PYOBJECTPTR_H
+#define PYOBJECTPTR_H
+
+#include "PyObjectDecl.h"
+#include "Result.h"  // Status
+#include <vector>
+
+//========================================
+
+// safe container for PyObjects
+/*
+The class `PyObjectPtr` contains a `PyObject*` (or `NULL`) and a `Status` which denotes the status of the value (OK/Warning/Error).
+Decrementing Python reference-count is performed automatically when a
+`PyObjectPtr` expires.
+*/
+
+class PyObjectPtr
+{
+
+public:
+    // raw pointer to the PyObject
+    PyObject* ptr = nullptr;
+
+    // status of the PyObject
+    Status status;
+
+    PyObjectPtr(PyObject* pyobject_ptr, const Status& status);
+
+    PyObjectPtr(PyObject* pyobject_ptr);
+
+    PyObjectPtr(const Status& status);
+
+    ~PyObjectPtr();
+
+    // disallow copy constructor
+    PyObjectPtr(const PyObjectPtr&) = delete;
+    // disallow copy assignment
+    PyObjectPtr& operator=(const PyObjectPtr&) = delete;
+    // allow move constructor
+    PyObjectPtr(PyObjectPtr&& other);
+    // reset the container to the initial status (does _not_ release the Python resource)
+    void reset();
+    // reset the container to the initial status and return the PyObject pointer (does _not_ release the Python resource)
+    PyObject* release();
+    // discards the Python resource (decrements the Python reference)
+    void discard();
+    // check validity of the PyObjectPtr
+    bool valid() const;
+    // check if the PyObjectPtr is set
+    bool isSet() const;
+};
+
+
+// Numpy array descriptor
+struct NpArrayDescr
+{
+    // Numpy array descriptors
+    bool C_contiguous = false;
+    bool F_contiguous = false;
+    // whether the data area of arr is aligned and in machine byte-order
+    bool wellbehaved = false;
+    // number of dimensions
+    int n_dims = 0;
+    // sizes of dimensions
+    std::vector<np_size_t> dims;
+    // character code indicating the data type
+    char dtype = '\0';
+    // whether Numpy array owns its data
+    bool owns_data = false;
+};
+
+#endif  // PYOBJECTPTR_H