@@ -1276,13 +1276,18 @@ def resource_to_stream(self, uri: str, stream: Any) -> int:
12761276class Signer :
12771277 """High-level wrapper for C2PA Signer operations."""
12781278
1279- def __init__ (self , signer_ptr : ctypes .POINTER (C2paSigner )):
1279+ def __init__ (self , signer_ptr : ctypes .POINTER (C2paSigner ), callback_cb : Optional [ SignerCallback ] = None ):
12801280 """Initialize a new Signer instance.
12811281
12821282 Note: This constructor is not meant to be called directly.
12831283 Use from_info() or from_callback() instead.
1284+
1285+ Args:
1286+ signer_ptr: Pointer to the C2PA signer
1287+ callback_cb: Optional callback function (for callback-based signers)
12841288 """
12851289 self ._signer = signer_ptr
1290+ self ._callback_cb = callback_cb # Keep callback alive to prevent garbage collection
12861291 self ._closed = False
12871292 self ._error_messages = {
12881293 'closed_error' : "Signer is closed" ,
@@ -1373,11 +1378,10 @@ def wrapped_callback(
13731378 data_len ,
13741379 signed_bytes_ptr ,
13751380 signed_len ):
1376- # Returns 0 on error as this case is handled in the native code gracefully
1381+ # Returns -1 on error as it is what the native code expects.
13771382 # The reason is that otherwise we ping-pong errors between native code and Python code,
13781383 # which can become tedious in handling. So we let the native code deal with it and
1379- # raise the errors accordingly, since it already checks the
1380- # signature length for correctness.
1384+ # raise the errors accordingly, since it already does checks.
13811385 try :
13821386 if not data_ptr or data_len <= 0 :
13831387 # Error: invalid input, invalid so return -1,
@@ -1398,8 +1402,8 @@ def wrapped_callback(
13981402 # native code will handle that too!
13991403 return - 1
14001404
1401- # Copy the signature back to the C buffer (since callback is
1402- # used in native code)
1405+ # Copy the signature back to the C buffer
1406+ # (since callback is used in native code)
14031407 actual_len = min (len (signature ), signed_len )
14041408 # Use memmove for efficient memory copying instead of byte-by-byte loop
14051409 ctypes .memmove (signed_bytes_ptr , signature , actual_len )
@@ -1423,15 +1427,13 @@ def wrapped_callback(
14231427 error_messages ['encoding_error' ].format (
14241428 str (e )))
14251429
1426- # Create the signer with the wrapped callback
1427- # Store the callback as an instance attribute to keep it alive, as this prevents
1428- # garbage collection and lifetime issues.
1429- signer_instance = cls .__new__ (cls )
1430- signer_instance ._callback_cb = SignerCallback (wrapped_callback )
1430+ # Create the callback object using the callback function
1431+ callback_cb = SignerCallback (wrapped_callback )
14311432
1433+ # Create the signer with the wrapped callback
14321434 signer_ptr = _lib .c2pa_signer_create (
14331435 None ,
1434- signer_instance . _callback_cb ,
1436+ callback_cb ,
14351437 alg ,
14361438 certs_bytes ,
14371439 tsa_url_bytes
@@ -1443,12 +1445,8 @@ def wrapped_callback(
14431445 raise C2paError (error )
14441446 raise C2paError ("Failed to create signer" )
14451447
1446- # Initialize the signer instance
1447- signer_instance ._signer = signer_ptr
1448- signer_instance ._closed = False
1449- signer_instance ._error_messages = error_messages
1450-
1451- return signer_instance
1448+ # Create and return the signer instance with the callback
1449+ return cls (signer_ptr , callback_cb )
14521450
14531451 def __enter__ (self ):
14541452 """Context manager entry."""
@@ -1888,6 +1886,7 @@ def _sign_internal(
18881886 source_stream .close ()
18891887 dest_stream .close ()
18901888
1889+
18911890 def sign (
18921891 self ,
18931892 signer : Signer ,
0 commit comments