Skip to content

Commit 23f44d6

Browse files
committed
fix: Missing symbol
1 parent 21f5be7 commit 23f44d6

File tree

2 files changed

+89
-45
lines changed

2 files changed

+89
-45
lines changed

src/c2pa/c2pa.py

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,33 +1725,29 @@ def to_archive(self, stream: Any):
17251725
raise C2paError(
17261726
self._error_messages['archive_error'].format("Unknown error"))
17271727

1728-
def sign(
1728+
def _sign_internal(
17291729
self,
17301730
signer: Signer,
17311731
format: str,
1732-
source: Any,
1733-
dest: Any = None) -> Optional[bytes]:
1734-
"""Sign the builder's content and write to a destination stream.
1735-
1732+
source_stream: Stream,
1733+
dest_stream: Stream) -> tuple[int, Optional[bytes]]:
1734+
"""Core signing logic shared between sign() and sign_file() methods.
1735+
17361736
Args:
1737-
format: The MIME type or extension of the content
1738-
source: The source stream (any Python stream-like object)
1739-
dest: The destination stream (any Python stream-like object)
17401737
signer: The signer to use
1741-
1738+
format: The MIME type or extension of the content
1739+
source_stream: The source stream
1740+
dest_stream: The destination stream
1741+
17421742
Returns:
17431743
A tuple of (size of C2PA data, optional manifest bytes)
1744-
1744+
17451745
Raises:
17461746
C2paError: If there was an error during signing
17471747
"""
17481748
if not self._builder:
17491749
raise C2paError(self._error_messages['closed_error'])
17501750

1751-
# Convert Python streams to Stream objects
1752-
source_stream = Stream(source)
1753-
dest_stream = Stream(dest)
1754-
17551751
try:
17561752
format_str = format.encode('utf-8')
17571753
manifest_bytes_ptr = ctypes.POINTER(ctypes.c_ubyte)()
@@ -1777,12 +1773,40 @@ def sign(
17771773
manifest_bytes = bytes(manifest_bytes_ptr[:size])
17781774
_lib.c2pa_manifest_bytes_free(manifest_bytes_ptr)
17791775

1780-
return manifest_bytes
1776+
return result, manifest_bytes
17811777
finally:
17821778
# Ensure both streams are cleaned up
17831779
source_stream.close()
17841780
dest_stream.close()
17851781

1782+
def sign(
1783+
self,
1784+
signer: Signer,
1785+
format: str,
1786+
source: Any,
1787+
dest: Any = None) -> Optional[bytes]:
1788+
"""Sign the builder's content and write to a destination stream.
1789+
1790+
Args:
1791+
format: The MIME type or extension of the content
1792+
source: The source stream (any Python stream-like object)
1793+
dest: The destination stream (any Python stream-like object)
1794+
signer: The signer to use
1795+
1796+
Returns:
1797+
The manifest bytes if available, None otherwise
1798+
1799+
Raises:
1800+
C2paError: If there was an error during signing
1801+
"""
1802+
# Convert Python streams to Stream objects
1803+
source_stream = Stream(source)
1804+
dest_stream = Stream(dest)
1805+
1806+
# Use the core signing logic
1807+
_, manifest_bytes = self._sign_internal(signer, format, source_stream, dest_stream)
1808+
return manifest_bytes
1809+
17861810
def sign_file(self,
17871811
source_path: Union[str,
17881812
Path],
@@ -1795,41 +1819,27 @@ def sign_file(self,
17951819
Args:
17961820
source_path: Path to the source file
17971821
dest_path: Path to write the signed file to
1822+
signer: The signer to use
17981823
17991824
Returns:
18001825
A tuple of (size of C2PA data, optional manifest bytes)
18011826
18021827
Raises:
18031828
C2paError: If there was an error during signing
18041829
"""
1805-
if not self._builder:
1806-
raise C2paError(self._error_messages['closed_error'])
1807-
1808-
source_path_str = str(source_path).encode('utf-8')
1809-
dest_path_str = str(dest_path).encode('utf-8')
1810-
manifest_bytes_ptr = ctypes.POINTER(ctypes.c_ubyte)()
1811-
1812-
result = _lib.c2pa_builder_sign_file(
1813-
self._builder,
1814-
source_path_str,
1815-
dest_path_str,
1816-
signer._signer,
1817-
ctypes.byref(manifest_bytes_ptr)
1818-
)
1830+
# Get the MIME type from the file extension
1831+
mime_type = mimetypes.guess_type(str(source_path))[0]
1832+
if not mime_type:
1833+
raise C2paError.NotSupported(f"Could not determine MIME type for file: {source_path}")
18191834

1820-
if result < 0:
1821-
error = _parse_operation_result_for_error(_lib.c2pa_error())
1822-
if error:
1823-
raise C2paError(error)
1824-
1825-
manifest_bytes = None
1826-
if manifest_bytes_ptr:
1827-
# Convert the manifest bytes to a Python bytes object
1828-
size = result
1829-
manifest_bytes = bytes(manifest_bytes_ptr[:size])
1830-
_lib.c2pa_manifest_bytes_free(manifest_bytes_ptr)
1835+
# Open source and destination files
1836+
with open(source_path, 'rb') as source_file, open(dest_path, 'wb') as dest_file:
1837+
# Convert Python streams to Stream objects
1838+
source_stream = Stream(source_file)
1839+
dest_stream = Stream(dest_file)
18311840

1832-
return result, manifest_bytes
1841+
# Use the core signing logic
1842+
return self._sign_internal(signer, mime_type, source_stream, dest_stream)
18331843

18341844

18351845
def format_embeddable(format: str, manifest_bytes: bytes) -> tuple[int, bytes]:

tests/test_unit_tests.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ def test_builder_double_close(self):
404404
# Verify builder is closed
405405
with self.assertRaises(Error):
406406
builder.set_no_embed()
407-
407+
408408
def test_builder_add_ingredient_on_closed_builder(self):
409409
"""Test that exception is raised when trying to add ingredient after close."""
410410
builder = Builder(self.manifestDefinition)
@@ -709,12 +709,46 @@ def test_builder_set_remote_url_no_embed(self):
709709
output.seek(0)
710710
with self.assertRaises(Error) as e:
711711
Reader("image/jpeg", output)
712-
712+
713713
self.assertIn("http://this_does_not_exist/foo.jpg", e.exception.message)
714-
714+
715715
# Return back to default settings
716716
load_settings(r'{"verify": { "remote_manifest_fetch": true} }')
717-
717+
718+
def test_sign_file(self):
719+
"""Test signing a file using the sign_file method."""
720+
import tempfile
721+
import shutil
722+
723+
# Create a temporary directory for the test
724+
temp_dir = tempfile.mkdtemp()
725+
try:
726+
# Create a temporary output file path
727+
output_path = os.path.join(temp_dir, "signed_output.jpg")
728+
729+
# Use the sign_file method
730+
builder = Builder(self.manifestDefinition)
731+
result, manifest_bytes = builder.sign_file(
732+
source_path=self.testPath,
733+
dest_path=output_path,
734+
signer=self.signer
735+
)
736+
737+
# Verify the output file was created
738+
self.assertTrue(os.path.exists(output_path))
739+
740+
# Read the signed file and verify the manifest
741+
with open(output_path, "rb") as file:
742+
reader = Reader("image/jpeg", file)
743+
json_data = reader.json()
744+
self.assertIn("Python Test", json_data)
745+
self.assertNotIn("validation_status", json_data)
746+
747+
finally:
748+
# Clean up the temporary directory
749+
shutil.rmtree(temp_dir)
750+
751+
718752
class TestStream(unittest.TestCase):
719753
def setUp(self):
720754
# Create a temporary file for testing

0 commit comments

Comments
 (0)