Skip to content

Commit 3b6b08e

Browse files
committed
Guard C-API teardown during interpreter shutdown
1 parent d9ffc20 commit 3b6b08e

2 files changed

Lines changed: 33 additions & 8 deletions

File tree

src_py/_lbug_capi.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -995,8 +995,10 @@ def __init__(
995995
_check_state(state, "Failed to initialize database")
996996

997997
def close(self) -> None:
998+
lib = _LIB
998999
if self._database._database:
999-
_LIB.lbug_database_destroy(ctypes.byref(self._database))
1000+
if lib is not None:
1001+
lib.lbug_database_destroy(ctypes.byref(self._database))
10001002
self._database._database = None
10011003

10021004
@staticmethod
@@ -1024,8 +1026,10 @@ def __init__(self, prepared: _LbugPreparedStatement):
10241026
self._prepared = prepared
10251027

10261028
def close(self) -> None:
1029+
lib = _LIB
10271030
if self._prepared._prepared_statement:
1028-
_LIB.lbug_prepared_statement_destroy(ctypes.byref(self._prepared))
1031+
if lib is not None:
1032+
lib.lbug_prepared_statement_destroy(ctypes.byref(self._prepared))
10291033
self._prepared._prepared_statement = None
10301034

10311035
def is_success(self) -> bool:
@@ -1076,16 +1080,21 @@ def _adopt_blob(self, ptr: ctypes.POINTER(ctypes.c_uint8), length: int) -> bytes
10761080
return bytes(ctypes.string_at(ptr, length))
10771081

10781082
def close(self) -> None:
1079-
for ptr in self._owned_string_ptrs:
1080-
_LIB.lbug_destroy_string(ptr)
1083+
lib = _LIB
1084+
1085+
if lib is not None:
1086+
for ptr in self._owned_string_ptrs:
1087+
lib.lbug_destroy_string(ptr)
10811088
self._owned_string_ptrs.clear()
10821089

1083-
for ptr in self._owned_blob_ptrs:
1084-
_LIB.lbug_destroy_blob(ptr)
1090+
if lib is not None:
1091+
for ptr in self._owned_blob_ptrs:
1092+
lib.lbug_destroy_blob(ptr)
10851093
self._owned_blob_ptrs.clear()
10861094

10871095
if self._result._query_result:
1088-
_LIB.lbug_query_result_destroy(ctypes.byref(self._result))
1096+
if lib is not None:
1097+
lib.lbug_query_result_destroy(ctypes.byref(self._result))
10891098
self._result._query_result = None
10901099

10911100
def __del__(self) -> None:
@@ -1764,8 +1773,10 @@ def __init__(self, database: Database, num_threads: int = 0):
17641773
self.set_max_threads_for_exec(num_threads)
17651774

17661775
def close(self) -> None:
1776+
lib = _LIB
17671777
if self._connection._connection:
1768-
_LIB.lbug_connection_destroy(ctypes.byref(self._connection))
1778+
if lib is not None:
1779+
lib.lbug_connection_destroy(ctypes.byref(self._connection))
17691780
self._connection._connection = None
17701781

17711782
def set_max_threads_for_exec(self, num_threads: int) -> None:

test/test_capi_backend.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from datetime import date, datetime
44

55
import ladybug as lb
6+
import ladybug._lbug_capi as lbug_capi
7+
import pytest
68

79

810
def test_capi_backend_basic_query() -> None:
@@ -36,3 +38,15 @@ def test_capi_backend_parameter_binding() -> None:
3638

3739
conn.close()
3840
db.close()
41+
42+
43+
def test_capi_close_tolerates_lib_teardown(monkeypatch: pytest.MonkeyPatch) -> None:
44+
db = lb.Database(":memory:", backend="capi")
45+
conn = lb.Connection(db)
46+
result = conn.execute("RETURN 1 AS a;")
47+
48+
monkeypatch.setattr(lbug_capi, "_LIB", None)
49+
50+
result.close()
51+
conn.close()
52+
db.close()

0 commit comments

Comments
 (0)