Skip to content

Commit e108798

Browse files
authored
fix: Improve error clearing (#184)
* fix: CLean up errors * fix: Error cleanup on isntantiate/new use * fix: Clarify comment
1 parent 87a0ee4 commit e108798

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

src/c2pa/c2pa.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,19 @@ class C2paStream(ctypes.Structure):
244244
]
245245

246246

247+
def _clear_error_state():
248+
"""Clear any existing error state from the C library.
249+
250+
This function should be called at the beginning of object initialization
251+
and before any operations that could potentially raise an error,
252+
to ensure that stale error states from previous operations don't interfere
253+
with new objects being created, or independent function calls.
254+
"""
255+
error = _lib.c2pa_error()
256+
if error:
257+
# Free the error to clear the state
258+
_lib.c2pa_string_free(error)
259+
247260
class C2paSignerInfo(ctypes.Structure):
248261
"""Configuration for a Signer."""
249262
_fields_ = [
@@ -264,6 +277,7 @@ def __init__(self, alg, sign_cert, private_key, ta_url):
264277
private_key: The private key as a string
265278
ta_url: The timestamp authority URL as bytes
266279
"""
280+
_clear_error_state()
267281

268282
if sign_cert is None:
269283
raise ValueError("sign_cert must be set")
@@ -692,6 +706,8 @@ def load_settings(settings: Union[str, dict], format: str = "json") -> None:
692706
Raises:
693707
C2paError: If there was an error loading the settings
694708
"""
709+
_clear_error_state()
710+
695711
# Convert to JSON string as necessary
696712
try:
697713
if isinstance(settings, dict):
@@ -776,6 +792,8 @@ def read_ingredient_file(
776792
stacklevel=2,
777793
)
778794

795+
_clear_error_state()
796+
779797
container = _StringContainer()
780798

781799
container._path_str = str(path).encode('utf-8')
@@ -821,6 +839,8 @@ def read_file(path: Union[str, Path],
821839
stacklevel=2,
822840
)
823841

842+
_clear_error_state()
843+
824844
container = _StringContainer()
825845

826846
container._path_str = str(path).encode('utf-8')
@@ -907,6 +927,8 @@ def sign_file(
907927
stacklevel=2,
908928
)
909929

930+
_clear_error_state()
931+
910932
try:
911933
# Determine if we have a signer or signer info
912934
if isinstance(signer_or_info, C2paSignerInfo):
@@ -1375,6 +1397,9 @@ def __init__(self,
13751397
C2paError.Encoding: If any of the string inputs
13761398
contain invalid UTF-8 characters
13771399
"""
1400+
# Native libs plumbing:
1401+
# Clear any stale error state from previous operations
1402+
_clear_error_state()
13781403

13791404
self._closed = False
13801405
self._initialized = False
@@ -1930,6 +1955,10 @@ def from_info(cls, signer_info: C2paSignerInfo) -> 'Signer':
19301955
Raises:
19311956
C2paError: If there was an error creating the signer
19321957
"""
1958+
# Native libs plumbing:
1959+
# Clear any stale error state from previous operations
1960+
_clear_error_state()
1961+
19331962
signer_ptr = _lib.c2pa_signer_from_info(ctypes.byref(signer_info))
19341963

19351964
if not signer_ptr:
@@ -2055,6 +2084,10 @@ def wrapped_callback(
20552084
cls._ERROR_MESSAGES['encoding_error'].format(
20562085
str(e)))
20572086

2087+
# Native libs plumbing:
2088+
# Clear any stale error state from previous operations
2089+
_clear_error_state()
2090+
20582091
# Create the callback object using the callback function
20592092
callback_cb = SignerCallback(wrapped_callback)
20602093

@@ -2095,6 +2128,10 @@ def __init__(self, signer_ptr: ctypes.POINTER(C2paSigner)):
20952128
Raises:
20962129
C2paError: If the signer pointer is invalid
20972130
"""
2131+
# Native libs plumbing:
2132+
# Clear any stale error state from previous operations
2133+
_clear_error_state()
2134+
20982135
# Validate pointer before assignment
20992136
if not signer_ptr:
21002137
raise C2paError("Invalid signer pointer: pointer is null")
@@ -2327,6 +2364,7 @@ def from_archive(cls, stream: Any) -> 'Builder':
23272364
"""
23282365
builder = cls({})
23292366
stream_obj = Stream(stream)
2367+
23302368
builder._builder = _lib.c2pa_builder_from_archive(stream_obj._stream)
23312369

23322370
if not builder._builder:
@@ -2352,6 +2390,10 @@ def __init__(self, manifest_json: Any):
23522390
C2paError.Encoding: If manifest JSON contains invalid UTF-8 chars
23532391
C2paError.Json: If the manifest JSON cannot be serialized
23542392
"""
2393+
# Native libs plumbing:
2394+
# Clear any stale error state from previous operations
2395+
_clear_error_state()
2396+
23552397
self._closed = False
23562398
self._initialized = False
23572399
self._builder = None
@@ -2884,6 +2926,8 @@ def format_embeddable(format: str, manifest_bytes: bytes) -> tuple[int, bytes]:
28842926
Raises:
28852927
C2paError: If there was an error converting the manifest
28862928
"""
2929+
_clear_error_state()
2930+
28872931
format_str = format.encode('utf-8')
28882932
manifest_array = (ctypes.c_ubyte * len(manifest_bytes))(*manifest_bytes)
28892933
result_bytes_ptr = ctypes.POINTER(ctypes.c_ubyte)()
@@ -2993,6 +3037,8 @@ def ed25519_sign(data: bytes, private_key: str) -> bytes:
29933037
C2paError: If there was an error signing the data
29943038
C2paError.Encoding: If the private key contains invalid UTF-8 chars
29953039
"""
3040+
_clear_error_state()
3041+
29963042
if not data:
29973043
raise C2paError("Data to sign cannot be empty")
29983044

0 commit comments

Comments
 (0)