将 pybind11-binding 标记为已弃用的最佳方法

Best way to mark a pybind11-binding as deprecated

我有一个 C++ class,使用 pybind11 Python 绑定。

现在我想将一个方法的绑定标记为已弃用。让我们假设它看起来像这样:

PYBIND11_MODULE(my_module, m)
{
    pybind11::class_<Foobar>(m, "PyFoobar")
        .def("old_foo", &Foobar::foo)  // <-- this is deprecated in favour of "new_foo"
        .def("new_foo", &Foobar::foo);
}

PyFoobar.old_foo() 标记为已弃用以便用户在调用该方法时注意到它的最佳方法是什么?理想情况下,我希望触发 DeprecationWarning

我找到了一些方法来获得我想要的大部分内容:对已弃用的绑定使用 lambda。在此 lambda 中,发出警告,然后调用实际函数。在我的示例中,唯一的变化是名称,我只是在 old_foo 中调用 new_foo。如果绑定的实际函数不同,这将变得更加复杂。

PYBIND11_MODULE(my_module, m)
{
    pybind11::class_<Foobar>(m, "PyFoobar")
        .def("old_foo",
             [](pybind11::object &self, int arg_to_foo)
             {
                 auto warnings = pybind11::module::import("warnings");
                 warnings.attr("warn")(
                     "old_foo() is deprecated, use new_foo() instead.");

                 return self.attr("new_foo")(arg_to_foo);
             })
        .def("new_foo", &Foobar::foo);
}

这导致

UserWarning: old_foo() is deprecated, use new_foo() instead.

第一次调用old_foo()

不幸的是,我还没有弄清楚如何使它成为 DeprecationWarning 而不是 UserWarning

好的,这是我的工作示例。我实际上想不出用 Python C 类型调用导入的 python 函数,所以我直接使用 C API,无论如何它应该表现更好

struct Foobar {
  Foobar() {}

  void foo(int32_t art) {}

};

PYBIND11_MODULE(example, m) {

  pybind11::class_<Foobar>(m, "PyFoobar")
    .def(py::init<>())
    .def("old_foo",
          [](pybind11::object &self, int arg_to_foo)
          {
            PyErr_WarnEx(PyExc_DeprecationWarning, 
                         "old_foo() is deprecated, use new_foo() instead.", 
                         1);
              return self.attr("new_foo")(arg_to_foo);
          })
    .def("new_foo", &Foobar::foo);
}

您可以从此处将任何警告类型作为第一个参数传递: https://docs.python.org/3/c-api/exceptions.html#standard-warning-categories

最终的 int 是您希望标记为已弃用的堆栈级别。

所以看看这个 python 代码

import example

import warnings

warnings.simplefilter("default")

def mary():
    f = example.PyFoobar()
    f.old_foo(1)

mary()

如果您将堆栈级别设置为 1,您将获得

test.py:9: DeprecationWarning: old_foo() is deprecated, use new_foo() instead.
  f.old_foo(1)

您可能需要 2 它将为您提供实际的调用上下文,但取决于您的用例是什么

test.py:11: DeprecationWarning: old_foo() is deprecated, use new_foo() instead.
  mary()

同样重要的是要注意,默认情况下,python 的许多版本都关闭了弃用警告。您可以通过检查的值来检查这一点 warnings.filters。这就是为什么在我的示例中我调用 warnings.simplefilter("default") 启用所有类型的警告,但仅在它们第一次被击中时。还有其他方法可以控制它,包括在 运行 python 或环境变量时使用 -W 标志。 https://docs.python.org/3/library/warnings.html#describing-warning-filters