From 413bd9944b2d5b396a6ad490ddbee417c773cde9 Mon Sep 17 00:00:00 2001
From: Enric Tejedor Saavedra <enric.tejedor.saavedra@cern.ch>
Date: Thu, 6 Dec 2018 13:51:53 +0100
Subject: [PATCH] [ROOT-9809] Only invoke Python C API if Python is initialized

If libPyROOT is loaded with gSystem->Load, the static
initialization block in TMemoryRegulator.cxx is executed and
ends up invoking PyCFunction_New, which causes a crash from
Python 3.7. The crash is due to Python not being initialized.
This also happens when using TPython from C++, since Python
has not	been initialized when TPython is used.

Note that when loading libPyROOT from ROOT.py, which is what
happens when someone uses PyROOT, the Python interpreter already
exists and is initialized, so invoking PyCFunction_New does not
crash.

This fix moves the creation of gObjectEraseCallback away from
the static block in TMemoryRegulator in order to prevent the
issue described above.
---
 bindings/pyroot/src/TMemoryRegulator.cxx | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/bindings/pyroot/src/TMemoryRegulator.cxx b/bindings/pyroot/src/TMemoryRegulator.cxx
index 1a71a6a2ffb..270fe4d223a 100644
--- a/bindings/pyroot/src/TMemoryRegulator.cxx
+++ b/bindings/pyroot/src/TMemoryRegulator.cxx
@@ -20,16 +20,13 @@ PyROOT::TMemoryRegulator::WeakRefMap_t* PyROOT::TMemoryRegulator::fgWeakRefTable
 namespace {
 
 // memory regulater callback for deletion of registered objects
-   PyMethodDef methoddef_ = {
+   PyMethodDef gObjectEraseMethodDef = {
       const_cast< char* >( "TMemoryRegulator_internal_ObjectEraseCallback" ),
       (PyCFunction) PyROOT::TMemoryRegulator::ObjectEraseCallback,
       METH_O,
       NULL
    };
 
-   PyObject* gObjectEraseCallback = PyCFunction_New( &methoddef_, NULL );
-
-
 // pseudo-None type for masking out objects on the python side
    PyTypeObject PyROOT_NoneType;
 
@@ -199,13 +196,15 @@ void PyROOT::TMemoryRegulator::RecursiveRemove( TObject* object )
 
 Bool_t PyROOT::TMemoryRegulator::RegisterObject( ObjectProxy* pyobj, TObject* object )
 {
+   static PyObject* objectEraseCallback = PyCFunction_New(&gObjectEraseMethodDef, nullptr);
+
    if ( ! ( pyobj && object ) )
       return kFALSE;
 
    ObjectMap_t::iterator ppo = fgObjectTable->find( object );
    if ( ppo == fgObjectTable->end() ) {
       object->SetBit( TObject::kMustCleanup );
-      PyObject* pyref = PyWeakref_NewRef( (PyObject*)pyobj, gObjectEraseCallback );
+      PyObject* pyref = PyWeakref_NewRef( (PyObject*)pyobj, objectEraseCallback );
       ObjectMap_t::iterator newppo = fgObjectTable->insert( std::make_pair( object, pyref ) ).first;
       (*fgWeakRefTable)[ pyref ] = newppo;  // no Py_INCREF on pyref, as object table has one
       return kTRUE;
-- 
GitLab