Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyBind11 destructor not invoked?

I have a c++ class wrapped with PyBind11. The issue is: when the Python script ends the c++ destructor is not being automatically invoked. This causes an untidy exit because networking resources need to be released by the destructor.

As a work-around it is necessary to explicitly delete the Python object, but I don't understand why!

Please could someone explain what is wrong here and how to get the destructor called automatically when the Python object is garbage collected?

Pybind11 binding code:

py::class_<pcs::Listener>(m, "listener")
    .def(py::init<const py::object &, const std::string &, const std::string &, const std::string &, const std::string &, const std::set<std::string> &, const std::string & , const bool & , const bool & >(), R"pbdoc(
    Monitors network traffic.

    When a desired data source is detected a client instance is connected to consume the data stream.

    Reconstructs data on receipt, like a jigsaw.  Makes requests to fill any gaps.  Verifies the data as sequential.

    Data is output by callback to Python.  Using the method specified in the constructor, which must accept a string argument.
)pbdoc");

In Python:

#Function to callback
def print_string(str):
    print("Python; " + str)

lstnr = listener(print_string, 'tcp://127.0.0.1:9001', clientCertPath, serverCertPath, proxyCertPath, desiredSources, 'time_series_data', enableCurve, enableVerbose)

#Run for a minute
cnt = 0
while cnt < 60:
    cnt += 1
    time.sleep(1)

#Need to call the destructor explicity for some reason    
del lstnr
like image 769
GoFaster Avatar asked Oct 26 '25 00:10

GoFaster


1 Answers

So some years later I fixed this issue by enabling Python context manager with support by adding __enter__ and __exit__ method handling to my PyBind11 code:

py::class_<pcs::Listener>(m, "listener")
.def(py::init<const py::object &, const std::string &, const std::string &, const std::string &, const std::string &, const std::set<std::string> &, const std::string & , const bool & , const bool & >(), R"pbdoc(
    Monitors network traffic.

    When a desired data source is detected a client instance is connected to consume the data stream.
    
    Specify 'type' as 'string' or 'market_data' to facilitate appropriate handling of BarData or string messages.

    Reconstructs data on receipt, like a jigsaw.  Makes requests to fill any gaps.  Verifies the data as sequential.

    Data is output by callback to Python.  Using the method specified in the constructor, which must accept a string argument.
)pbdoc")
.def("__enter__", &pcs::Listener::enter, R"pbdoc(
    Python 'with' context manager support.
)pbdoc")    
.def("__exit__", &pcs::Listener::exit, R"pbdoc(
    Python 'with' context manager support.
)pbdoc");

Added corresponding functions to the C++ class, like so:

//For Python 'with' context manager
auto enter(){std::cout << "Context Manager: Enter" << std::endl; return py::cast(this); }//returns a pointer to this object for 'with'....'as' python functionality
auto exit(py::handle type, py::handle value, py::handle traceback){ std::cout << "Context Manager: Exit: " << type << " " << value << " " << traceback <<  std::endl; }

N.B.

  1. The returned pointer value from enter() is important to the as functionality in a with....as statement.

  2. The parameters passed to exit(py::handle type, py::handle value, py::handle traceback) are useful debugging info.

Python usage:

with listener(cb, endpoint, clientCertPath, serverCertPath, proxyCertPath, desiredSources, type, enableCurve, enableVerbose):
    cnt = 0
    while cnt < 10:
        cnt += 1
        time.sleep(1)

The Python context manager now calls the pcs::Listener::exit on the C++ object, which should release the networking resources.

like image 187
GoFaster Avatar answered Oct 28 '25 13:10

GoFaster



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!