Skip to content

Commit f987e61

Browse files
committed
fix: Signer clean up
1 parent 41502d4 commit f987e61

File tree

1 file changed

+70
-16
lines changed

1 file changed

+70
-16
lines changed

src/c2pa/c2pa.py

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,7 +1679,17 @@ def __init__(self, signer_ptr: ctypes.POINTER(C2paSigner)):
16791679
16801680
Note: This constructor is not meant to be called directly.
16811681
Use from_info() or from_callback() instead.
1682+
1683+
Args:
1684+
signer_ptr: Pointer to the native C2PA signer
1685+
1686+
Raises:
1687+
C2paError: If the signer pointer is invalid
16821688
"""
1689+
# Validate pointer before assignment
1690+
if not signer_ptr:
1691+
raise C2paError("Invalid signer pointer: pointer is null")
1692+
16831693
self._signer = signer_ptr
16841694
self._closed = False
16851695

@@ -1854,40 +1864,85 @@ def wrapped_callback(
18541864

18551865
def __enter__(self):
18561866
"""Context manager entry."""
1857-
if self._closed:
1858-
raise C2paError(Signer._ERROR_MESSAGES['closed_error'])
1867+
self._ensure_valid_state()
1868+
1869+
# Additional pointer validation before entering context
1870+
if not self._signer:
1871+
raise C2paError("Invalid signer pointer: pointer is null")
1872+
18591873
return self
18601874

18611875
def __exit__(self, exc_type, exc_val, exc_tb):
18621876
"""Context manager exit."""
18631877
self.close()
18641878

1879+
def _cleanup_resources(self):
1880+
"""Internal cleanup method that safely releases native resources.
1881+
1882+
This method handles the actual cleanup logic and can be called
1883+
from both close() and __del__ without causing double frees.
1884+
"""
1885+
try:
1886+
if not self._closed and self._signer:
1887+
try:
1888+
_lib.c2pa_signer_free(self._signer)
1889+
except Exception:
1890+
# Log cleanup errors but don't raise exceptions
1891+
logger.warning("Failed to free native Signer resources")
1892+
finally:
1893+
self._signer = None
1894+
1895+
# Clean up callback reference
1896+
if self._callback_cb:
1897+
self._callback_cb = None
1898+
1899+
self._closed = True
1900+
except Exception:
1901+
# Ensure we don't raise exceptions during cleanup
1902+
pass
1903+
1904+
def _ensure_valid_state(self):
1905+
"""Ensure the signer is in a valid state for operations.
1906+
1907+
Raises:
1908+
C2paError: If the signer is closed or invalid
1909+
"""
1910+
if self._closed:
1911+
raise C2paError(Signer._ERROR_MESSAGES['closed_error'])
1912+
if not self._signer:
1913+
raise C2paError(Signer._ERROR_MESSAGES['closed_error'])
1914+
18651915
def close(self):
1866-
"""Release the signer resources.
1916+
"""Release the signer resources safely.
18671917
18681918
This method ensures all resources are properly cleaned up,
18691919
even if errors occur during cleanup.
1870-
Errors during cleanup are logged but not raised to ensure cleanup.
1871-
Multiple calls to close() are handled gracefully.
1920+
1921+
Note:
1922+
Multiple calls to close() are handled gracefully.
1923+
Errors during cleanup are logged but not raised
1924+
to ensure cleanup.
18721925
"""
18731926
if self._closed:
18741927
return
18751928

18761929
try:
1877-
if self._signer:
1878-
try:
1879-
_lib.c2pa_signer_free(self._signer)
1880-
except Exception as e:
1881-
logger.error(
1882-
Signer._ERROR_MESSAGES['signer_cleanup'].format(
1883-
str(e)))
1884-
finally:
1885-
self._signer = None
1930+
# Validate pointer before cleanup if it exists
1931+
if self._signer and self._signer != 0:
1932+
# Use the internal cleanup method
1933+
self._cleanup_resources()
1934+
else:
1935+
# Make sure to release the callback
1936+
if self._callback_cb:
1937+
self._callback_cb = None
1938+
18861939
except Exception as e:
1940+
# Log any unexpected errors during close
18871941
logger.error(
18881942
Signer._ERROR_MESSAGES['cleanup_error'].format(
18891943
str(e)))
18901944
finally:
1945+
# Always mark as closed, regardless of cleanup success
18911946
self._closed = True
18921947

18931948
def reserve_size(self) -> int:
@@ -1899,8 +1954,7 @@ def reserve_size(self) -> int:
18991954
Raises:
19001955
C2paError: If there was an error getting the size
19011956
"""
1902-
if self._closed or not self._signer:
1903-
raise C2paError(Signer._ERROR_MESSAGES['closed_error'])
1957+
self._ensure_valid_state()
19041958

19051959
result = _lib.c2pa_signer_reserve_size(self._signer)
19061960

0 commit comments

Comments
 (0)