From 5bcc847ec3eac857980e583b2edb4b733d4b532b Mon Sep 17 00:00:00 2001 From: Pavol Juhas Date: Thu, 15 Aug 2024 01:03:26 -0700 Subject: [PATCH 1/2] Use atexit for cleanup of Python-extended prototypes Use standard atexit handler to remove Python-extended prototype objects from C++ registry. Ensure cleanup is truly executed instead of relying on garbage collector to call `__del__`. This prevents the following error message on Python exit Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) --- src/diffpy/srreal/_cleanup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/diffpy/srreal/_cleanup.py b/src/diffpy/srreal/_cleanup.py index 7d7b0b42..dbb41b13 100644 --- a/src/diffpy/srreal/_cleanup.py +++ b/src/diffpy/srreal/_cleanup.py @@ -27,6 +27,7 @@ class prototypes is implemented in libdiffpy. Any Python-extended classes """ +import atexit import weakref @@ -62,7 +63,7 @@ def add(self, obj): return - def __del__(self): + def clean(self): while self._references: wr = self._references.pop() obj = wr() @@ -75,7 +76,8 @@ def __del__(self): # create singleton instance of the cleanup handler _cleanup_handler = _DerivedClassesCleanUpHandler() -del _DerivedClassesCleanUpHandler +atexit.register(_cleanup_handler.clean) +del _DerivedClassesCleanUpHandler # End of file. From 3a201f257e09fe186a4d78598c430af4cac47d03 Mon Sep 17 00:00:00 2001 From: Pavol Juhas Date: Thu, 15 Aug 2024 16:06:44 -0700 Subject: [PATCH 2/2] Fix test error on removed numpy.complex Can as well test with the builtin complex here. --- src/diffpy/srreal/tests/testpairquantity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diffpy/srreal/tests/testpairquantity.py b/src/diffpy/srreal/tests/testpairquantity.py index c54c27e9..2bf755ab 100644 --- a/src/diffpy/srreal/tests/testpairquantity.py +++ b/src/diffpy/srreal/tests/testpairquantity.py @@ -83,7 +83,7 @@ def test_setPairMask_args(self): spm = self.pq.setPairMask gpm = self.pq.getPairMask self.assertRaises(TypeError, spm, 0.0, 0, False) - self.assertRaises(TypeError, spm, numpy.complex(0.5), 0, False) + self.assertRaises(TypeError, spm, complex(0.5), 0, False) self.assertTrue(gpm(0, 0)) spm(numpy.int32(1), 0, True, others=False) self.assertTrue(gpm(0, 1))