Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/c2pa/c2pa.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,19 @@ class C2paStream(ctypes.Structure):
]


def _clear_error_state():
"""Clear any existing error state from the C library.

This function should be called at the beginning of object initialization
and before any operations that could potentially raise an error,
to ensure that stale error states from previous operations don't interfere
with new objects being created, or independent function calls.
"""
error = _lib.c2pa_error()
if error:
# Free the error to clear the state
_lib.c2pa_string_free(error)

class C2paSignerInfo(ctypes.Structure):
"""Configuration for a Signer."""
_fields_ = [
Expand All @@ -264,6 +277,7 @@ def __init__(self, alg, sign_cert, private_key, ta_url):
private_key: The private key as a string
ta_url: The timestamp authority URL as bytes
"""
_clear_error_state()

if sign_cert is None:
raise ValueError("sign_cert must be set")
Expand Down Expand Up @@ -692,6 +706,8 @@ def load_settings(settings: Union[str, dict], format: str = "json") -> None:
Raises:
C2paError: If there was an error loading the settings
"""
_clear_error_state()

# Convert to JSON string as necessary
try:
if isinstance(settings, dict):
Expand Down Expand Up @@ -776,6 +792,8 @@ def read_ingredient_file(
stacklevel=2,
)

_clear_error_state()

container = _StringContainer()

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

_clear_error_state()

container = _StringContainer()

container._path_str = str(path).encode('utf-8')
Expand Down Expand Up @@ -907,6 +927,8 @@ def sign_file(
stacklevel=2,
)

_clear_error_state()

try:
# Determine if we have a signer or signer info
if isinstance(signer_or_info, C2paSignerInfo):
Expand Down Expand Up @@ -1375,6 +1397,9 @@ def __init__(self,
C2paError.Encoding: If any of the string inputs
contain invalid UTF-8 characters
"""
# Native libs plumbing:
# Clear any stale error state from previous operations
_clear_error_state()

self._closed = False
self._initialized = False
Expand Down Expand Up @@ -1930,6 +1955,10 @@ def from_info(cls, signer_info: C2paSignerInfo) -> 'Signer':
Raises:
C2paError: If there was an error creating the signer
"""
# Native libs plumbing:
# Clear any stale error state from previous operations
_clear_error_state()

signer_ptr = _lib.c2pa_signer_from_info(ctypes.byref(signer_info))

if not signer_ptr:
Expand Down Expand Up @@ -2055,6 +2084,10 @@ def wrapped_callback(
cls._ERROR_MESSAGES['encoding_error'].format(
str(e)))

# Native libs plumbing:
# Clear any stale error state from previous operations
_clear_error_state()

# Create the callback object using the callback function
callback_cb = SignerCallback(wrapped_callback)

Expand Down Expand Up @@ -2095,6 +2128,10 @@ def __init__(self, signer_ptr: ctypes.POINTER(C2paSigner)):
Raises:
C2paError: If the signer pointer is invalid
"""
# Native libs plumbing:
# Clear any stale error state from previous operations
_clear_error_state()

# Validate pointer before assignment
if not signer_ptr:
raise C2paError("Invalid signer pointer: pointer is null")
Expand Down Expand Up @@ -2327,6 +2364,7 @@ def from_archive(cls, stream: Any) -> 'Builder':
"""
builder = cls({})
stream_obj = Stream(stream)

builder._builder = _lib.c2pa_builder_from_archive(stream_obj._stream)

if not builder._builder:
Expand All @@ -2352,6 +2390,10 @@ def __init__(self, manifest_json: Any):
C2paError.Encoding: If manifest JSON contains invalid UTF-8 chars
C2paError.Json: If the manifest JSON cannot be serialized
"""
# Native libs plumbing:
# Clear any stale error state from previous operations
_clear_error_state()

self._closed = False
self._initialized = False
self._builder = None
Expand Down Expand Up @@ -2884,6 +2926,8 @@ def format_embeddable(format: str, manifest_bytes: bytes) -> tuple[int, bytes]:
Raises:
C2paError: If there was an error converting the manifest
"""
_clear_error_state()

format_str = format.encode('utf-8')
manifest_array = (ctypes.c_ubyte * len(manifest_bytes))(*manifest_bytes)
result_bytes_ptr = ctypes.POINTER(ctypes.c_ubyte)()
Expand Down Expand Up @@ -2993,6 +3037,8 @@ def ed25519_sign(data: bytes, private_key: str) -> bytes:
C2paError: If there was an error signing the data
C2paError.Encoding: If the private key contains invalid UTF-8 chars
"""
_clear_error_state()

if not data:
raise C2paError("Data to sign cannot be empty")

Expand Down
Loading