@@ -308,6 +308,11 @@ def _clear_error_state():
308308 _lib .c2pa_string_free (error )
309309
310310
311+ def _free_native_ptr (ptr ):
312+ """Free a native pointer by casting it to c_void_p and calling c2pa_free."""
313+ _lib .c2pa_free (ctypes .cast (ptr , ctypes .c_void_p ))
314+
315+
311316class C2paSignerInfo (ctypes .Structure ):
312317 """Configuration for a Signer."""
313318 _fields_ = [
@@ -1393,12 +1398,7 @@ def _cleanup_resources(self):
13931398 and self ._settings
13941399 ):
13951400 try :
1396- _lib .c2pa_free (
1397- ctypes .cast (
1398- self ._settings ,
1399- ctypes .c_void_p
1400- )
1401- )
1401+ _free_native_ptr (self ._settings )
14021402 except Exception :
14031403 logger .error (
14041404 "Failed to free native"
@@ -1573,12 +1573,7 @@ def __init__(
15731573 # Free builder if build was not reached
15741574 if builder_ptr is not None :
15751575 try :
1576- _lib .c2pa_free (
1577- ctypes .cast (
1578- builder_ptr ,
1579- ctypes .c_void_p ,
1580- )
1581- )
1576+ _free_native_ptr (builder_ptr )
15821577 except Exception :
15831578 pass
15841579 raise
@@ -1677,12 +1672,7 @@ def _cleanup_resources(self):
16771672 and self ._context
16781673 ):
16791674 try :
1680- _lib .c2pa_free (
1681- ctypes .cast (
1682- self ._context ,
1683- ctypes .c_void_p ,
1684- )
1685- )
1675+ _free_native_ptr (self ._context )
16861676 except Exception :
16871677 logger .error (
16881678 "Failed to free native"
@@ -2487,12 +2477,7 @@ def _cleanup_resources(self):
24872477 # Clean up reader
24882478 if hasattr (self , '_reader' ) and self ._reader :
24892479 try :
2490- _lib .c2pa_free (
2491- ctypes .cast (
2492- self ._reader ,
2493- ctypes .c_void_p ,
2494- )
2495- )
2480+ _free_native_ptr (self ._reader )
24962481 except Exception :
24972482 # Cleanup failure doesn't raise exceptions
24982483 logger .error (
@@ -3068,12 +3053,7 @@ def _cleanup_resources(self):
30683053 self ._state = LifecycleState .CLOSED
30693054
30703055 try :
3071- _lib .c2pa_free (
3072- ctypes .cast (
3073- self ._signer ,
3074- ctypes .c_void_p ,
3075- )
3076- )
3056+ _free_native_ptr (self ._signer )
30773057 except Exception :
30783058 # Log cleanup errors but don't raise exceptions
30793059 logger .error ("Failed to free native Signer resources" )
@@ -3242,11 +3222,25 @@ def from_json(
32423222 """
32433223 return cls (manifest_json , context = context )
32443224
3225+ @classmethod
3226+ @overload
3227+ def from_archive (
3228+ cls ,
3229+ stream : Any ,
3230+ ) -> 'Builder' : ...
3231+
3232+ @classmethod
3233+ @overload
3234+ def from_archive (
3235+ cls ,
3236+ stream : Any ,
3237+ context : 'ContextProvider' ,
3238+ ) -> 'Builder' : ...
3239+
32453240 @classmethod
32463241 def from_archive (
32473242 cls ,
32483243 stream : Any ,
3249- * ,
32503244 context : Optional ['ContextProvider' ] = None ,
32513245 ) -> 'Builder' :
32523246 """Create a new Builder from an archive stream.
@@ -3378,7 +3372,6 @@ def _init_from_context(self, context, json_str):
33783372 new_ptr = _lib .c2pa_builder_with_definition (
33793373 builder_ptr , json_str ,
33803374 )
3381- # builder_ptr is NOW INVALID
33823375
33833376 if not new_ptr :
33843377 _parse_operation_result_for_error (
@@ -3434,12 +3427,7 @@ def _cleanup_resources(self):
34343427 self ,
34353428 '_builder' ) and self ._builder and self ._builder != 0 :
34363429 try :
3437- _lib .c2pa_free (
3438- ctypes .cast (
3439- self ._builder ,
3440- ctypes .c_void_p ,
3441- )
3442- )
3430+ _free_native_ptr (self ._builder )
34433431 except Exception :
34443432 # Log cleanup errors but don't raise exceptions
34453433 logger .error (
@@ -3783,7 +3771,7 @@ def _sign_internal(
37833771 source_stream: The source stream
37843772 dest_stream: The destination stream,
37853773 opened in w+b (write+read binary) mode.
3786- signer: Optional explicit signer . When None the context
3774+ signer: Signer to use . When None the context
37873775 signer is used instead.
37883776
37893777 Returns:
@@ -3833,12 +3821,15 @@ def _sign_internal(
38333821 manifest_bytes = b""
38343822 if manifest_bytes_ptr and result > 0 :
38353823 try :
3824+ # Convert the C pointer to Python bytes
38363825 temp_buffer = (ctypes .c_ubyte * result )()
38373826 ctypes .memmove (temp_buffer , manifest_bytes_ptr , result )
38383827 manifest_bytes = bytes (temp_buffer )
38393828 except Exception :
38403829 manifest_bytes = b""
38413830 finally :
3831+ # Always free the C-allocated memory,
3832+ # even if we failed to copy manifest bytes
38423833 try :
38433834 _lib .c2pa_manifest_bytes_free (manifest_bytes_ptr )
38443835 except Exception :
@@ -3908,10 +3899,6 @@ def sign(
39083899 ) -> bytes :
39093900 """Sign the builder's content with an explicit signer.
39103901
3911- Example::
3912-
3913- builder.sign(signer, "image/jpeg", source, dest)
3914-
39153902 Args:
39163903 signer: The signer to use.
39173904 format: The MIME type of the content.
@@ -3937,10 +3924,6 @@ def sign_with_context(
39373924 The builder must have been created with a Context
39383925 that has a signer.
39393926
3940- Example::
3941-
3942- builder.sign_with_context("image/jpeg", source, dest)
3943-
39443927 Args:
39453928 format: The MIME type of the content.
39463929 source: The source stream.
@@ -3994,25 +3977,17 @@ def sign_file(
39943977 Raises:
39953978 C2paError: If there was an error during signing
39963979 """
3997- mime_type = _get_mime_type_from_path (
3998- source_path
3999- )
3980+ mime_type = _get_mime_type_from_path (source_path )
40003981
40013982 try :
40023983 with (
40033984 open (source_path , 'rb' ) as source_file ,
40043985 open (dest_path , 'w+b' ) as dest_file ,
40053986 ):
40063987 if signer is not None :
4007- return self .sign (
4008- signer , mime_type ,
4009- source_file , dest_file ,
4010- )
4011- else :
4012- return self .sign (
4013- mime_type ,
4014- source_file , dest_file ,
4015- )
3988+ return self .sign (signer , mime_type , source_file , dest_file )
3989+ # else:
3990+ return self .sign (mime_type , source_file , dest_file )
40163991 except Exception as e :
40173992 raise C2paError (f"Error signing file: { str (e )} " ) from e
40183993
0 commit comments