Skip to content

Commit a1ac65b

Browse files
fix: __del__ not freeing up native resources
1 parent 5b8da6e commit a1ac65b

2 files changed

Lines changed: 28 additions & 6 deletions

File tree

extism/extism.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ def call(
625625
return parse(buf)
626626

627627
def __del__(self):
628-
if not hasattr(self, "pointer"):
628+
if not hasattr(self, "plugin") or self.plugin == -1:
629629
return
630630
_lib.extism_plugin_free(self.plugin)
631631
self.plugin = -1

tests/test_extism.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
from collections import namedtuple
2-
import unittest
3-
import extism
2+
import gc
43
import hashlib
54
import json
5+
import pickle
66
import time
7-
from threading import Thread
7+
import typing
8+
import unittest
89
from datetime import datetime, timedelta
910
from os.path import join, dirname
10-
import typing
11-
import pickle
11+
from threading import Thread
12+
from unittest.mock import patch
13+
14+
import extism
1215

1316

1417
# A pickle-able object.
@@ -47,6 +50,25 @@ def test_can_free_plugin(self):
4750
plugin = extism.Plugin(self._manifest())
4851
del plugin
4952

53+
def test_plugin_del_frees_native_resources(self):
54+
"""Test that Plugin.__del__ properly frees native resources.
55+
56+
This tests the fix for a bug where Plugin.__del__ checked for
57+
'self.pointer' instead of 'self.plugin', causing extism_plugin_free
58+
to never be called and leading to memory leaks.
59+
60+
This also tests that __del__ can be safely called multiple times
61+
(via context manager exit and garbage collection) without causing
62+
double-free errors.
63+
"""
64+
with extism.Plugin(self._manifest(), functions=[]) as plugin:
65+
j = json.loads(plugin.call("count_vowels", "test"))
66+
self.assertEqual(j["count"], 1)
67+
68+
# Verify plugin was freed after exiting context
69+
self.assertEqual(plugin.plugin, -1,
70+
"Expected plugin.plugin to be -1 after __del__, indicating extism_plugin_free was called")
71+
5072
def test_errors_on_bad_manifest(self):
5173
self.assertRaises(
5274
extism.Error, lambda: extism.Plugin({"invalid_manifest": True})

0 commit comments

Comments
 (0)