Лучший способ отметить привязку pybind11 как устаревшую - PullRequest
2 голосов
/ 19 июня 2020

У меня есть класс C ++ с привязками Python с использованием pybind11.

Теперь я хочу отметить привязку одного метода как устаревшую. Предположим, это выглядит примерно так:

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.

Ответы [ 2 ]

1 голос
/ 24 июня 2020

Хорошо, вот мой рабочий пример. На самом деле я не мог понять, как вызвать импортированную функцию python с типом Python C, поэтому я просто перешел прямо к 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"), который включает все типы предупреждений, но только при первом попадании. Есть и другие способы, которыми вы можете управлять этим, включая использование флага -W при запуске python или переменной среды. https://docs.python.org/3/library/warnings.html#describing -предупреждающие фильтры

0 голосов
/ 19 июня 2020

Я нашел способ получить большую часть того, что хочу: использовать лямбда-выражение для устаревшей привязки. В этой лямбде выдайте предупреждение, а затем вызовите фактическую функцию. Как и в моем примере, единственное изменение - это имя, я просто вызываю new_foo внутри old_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 () устарел, используйте new_foo () вместо этого.

При первом вызове old_foo().

К сожалению, я еще не придумал, как сделать DeprecationWarning вместо UserWarning.

...