Skip to content

Commit d09c247

Browse files
committed
python: fix dangling pointer and CSX state after Reset/ReadFromXML; add tests
- Reset() now nulls __CSX.thisptr and clears __CSX before calling C++ Reset, preventing the old Python wrapper from holding a freed pointer - ReadFromXML() clears the Python-side CSX reference inline before delegating to C++, avoiding an extra C++ Reset() call - GetCSX() nulls the old wrapper's thisptr when the C++ pointer has changed unexpectedly, as a safety net against unforeseen state changes - Add test_getcsx_not_none_after_read and test_getcsx_replaced_after_read to cover the fix from PR #218 and guard against regressions Signed-off-by: Thorsten Liebig <thorsten.liebig@gmx.de> Assisted-by: Claude Sonnet 4.6
1 parent 1619678 commit d09c247

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

python/Tests/test_XML_RoundTrip.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,39 @@ def test_read_resets_state(self):
161161
fdtd = openEMS(NrTS=9999)
162162
self.assertTrue(fdtd.ReadFromXML(self.fn))
163163

164+
def test_getcsx_not_none_after_read(self):
165+
fdtd = openEMS()
166+
fdtd.ReadFromXML(self.fn)
167+
self.assertIsNotNone(fdtd.GetCSX())
168+
169+
def test_getcsx_replaced_after_read(self):
170+
csx_before = _make_csx()
171+
csx_before.AddMetal('pre_existing_metal')
172+
mat = csx_before.AddMaterial('pre_existing_dielectric')
173+
mat.SetMaterialProperty(epsilon=99)
174+
csx_before.GetGrid().SetDeltaUnit(1.0) # intentionally wrong unit
175+
fdtd = openEMS()
176+
fdtd.SetCSX(csx_before)
177+
178+
fdtd.ReadFromXML(self.fn) # self.fn has no metals/dielectrics, DeltaUnit=1e-3
179+
180+
csx_after = fdtd.GetCSX()
181+
self.assertIsNotNone(csx_after)
182+
183+
# Grid delta unit must reflect the XML, not csx_before
184+
self.assertAlmostEqual(csx_after.GetGrid().GetDeltaUnit(), 1e-3)
185+
186+
fn_out = os.path.join(tempfile.gettempdir(), 'test_getcsx_replaced.xml')
187+
try:
188+
fdtd.Write2XML(fn_out)
189+
with open(fn_out) as f:
190+
xml_str = f.read()
191+
self.assertNotIn('pre_existing_metal', xml_str)
192+
self.assertNotIn('pre_existing_dielectric', xml_str)
193+
finally:
194+
if os.path.exists(fn_out):
195+
os.remove(fn_out)
196+
164197

165198
class Test_XML_RoundTrip(unittest.TestCase):
166199
"""Write → Read → Write cycle: second file should be identical to first."""

python/openEMS/openEMS.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ cdef class openEMS:
115115
self.__CSX.thisptr = NULL
116116

117117
def Reset(self):
118+
if self.__CSX is not None:
119+
self.__CSX.thisptr = NULL
120+
self.__CSX = None
118121
self.thisptr.Reset()
119122

120123
def SetNumberOfTimeSteps(self, val):
@@ -436,6 +439,8 @@ cdef class openEMS:
436439
return None
437440

438441
if self.__CSX is None or self.__CSX.thisptr != ptr:
442+
if self.__CSX is not None:
443+
self.__CSX.thisptr = NULL # prevent dangling pointer on the old wrapper
439444
csx = ContinuousStructure.__new__(ContinuousStructure)
440445
csx.thisptr = ptr
441446
self.__CSX = csx
@@ -579,4 +584,7 @@ cdef class openEMS:
579584
580585
:param file: xml file name
581586
"""
587+
if self.__CSX is not None:
588+
self.__CSX.thisptr = NULL
589+
self.__CSX = None
582590
return self.thisptr.ReadFromXML(file.encode('UTF-8'))

0 commit comments

Comments
 (0)