To ensure safely cleaning up PyObject* references simply use std::unique_ptr with a custom deleter:

# include <Python.h>
# include <memory>

auto py_deleter = [](PyObject* ptr) { Py_DECREF(ptr); };
using py_unique_ptr = std::unique_ptr<PyObject, decltype(py_deleter)>;

static PyObject* to_str(PyObject* self, PyObject* arg) {
  py_unique_ptr pyStr { PyObject_Str(arg) };
  if (/* some check */) {
    return nullptr; // no Py_DECREF needed, since the py_deleter gets called when pyStr leaves the scope.
  }
  return pyStr.release();
}

As soon as the unique_ptr leaves the scope it gets destroyed and py_deleter gets called with the contained pointer.
The return pyStr.release(); at end of the function releases the contained object without calling the py_deleter and returns it.

Since the deleter gets called only if the contained pointer isn’t null, you can safely use Py_DECREF instead of Py_XDECREF.

This way your code will get much more cleaner and you don’t have to care about decreasing references.