将 pybind11 绑定标记为已弃用的最佳方法

Posted

技术标签:

【中文标题】将 pybind11 绑定标记为已弃用的最佳方法【英文标题】:Best way to mark a pybind11-binding as deprecated 【发布时间】:2020-06-19 07:10:27 【问题描述】:

我有一个使用 pybind11 进行 Python 绑定的 C++ 类。

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

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

【问题讨论】:

【参考方案1】:

好的,这是我的工作示例。我实际上想不出用 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

【讨论】:

【参考方案2】:

我找到了一些方法来获得我想要的大部分内容:将 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);

这会导致

用户警告:old_foo() 已弃用,请改用 new_foo()。

old_foo()第一次被调用时。

不幸的是,我还没有弄清楚如何将其设置为 DeprecationWarning 而不是 UserWarning

【讨论】:

尝试将 PyExc_DeprecationWarning 作为第二个参数传递给“warn” @JesseC 看来我需要将PyExe_DeprecationWarning 转换为正确的类型。你知道怎么做吗?当我只是做warnings.attr("warn")("...", PyExe_DeprecationWarning); 时,我在运行时收到以下错误:RuntimeError: make_tuple(): unable to convert argument of type '_object*' to Python object

以上是关于将 pybind11 绑定标记为已弃用的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

将 PLSQL 过程/函数标记为已弃用

重命名python子包,将旧名称标记为已弃用

如何在 RAML 中将 REST 服务标记为已弃用

delphi 如何将属性标记为已弃用?

已弃用的 OpenGL 功能

推荐替换已弃用的 call_user_method?