Skip to content
Draft
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ build-from-source:

# Build API documentation with Sphinx
docs:
python3 scripts/generate_api_docs.py
$(PYTHON) scripts/generate_api_docs.py

# Memory profiling with memray (runs in Docker, reports go to tests/perf/reports/)
# More details for usage are in tests/perf/README.md
Expand Down
21 changes: 21 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Release notes

## Version 0.33.0

### Breaking changes

Removed the deprecated file-based free functions `read_file`,
`read_ingredient_file`, and `sign_file`, following the removal of the
underlying `c2pa_read_file`, `c2pa_read_ingredient_file`, and `c2pa_sign_file`
functions from the c2pa-rs C FFI. The deprecated `Builder.add_ingredient_from_file_path`
method was removed as well.

Migration:

- Instead of `read_file(path, data_dir)`, use `Reader(path).json()`.
- Instead of `read_ingredient_file(path, data_dir)`, use
`Builder.add_ingredient(json, format, stream)` to add the ingredient directly.
- Instead of the `sign_file(...)` free function, use the `Builder.sign_file(source_path, dest_path, signer)` method (or `Builder.sign(...)` for stream-based signing).
- Instead of `Builder.add_ingredient_from_file_path(json, format, filepath)`, open the
file and call `Builder.add_ingredient_from_stream(json, format, stream)`.

The `Builder.sign_file` method and `load_settings` are unaffected and remain available.

## Version 0.6.0

<!-- Get features and updates -->
Expand Down
2 changes: 0 additions & 2 deletions src/c2pa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
ContextBuilder,
ContextProvider,
sdk_version,
read_ingredient_file,
load_settings
) # NOQA

Expand All @@ -52,6 +51,5 @@
'ContextBuilder',
'ContextProvider',
'sdk_version',
'read_ingredient_file',
'load_settings'
]
259 changes: 0 additions & 259 deletions src/c2pa/c2pa.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@
'c2pa_error',
# Legacy APIs, deprecated
'c2pa_load_settings',
'c2pa_read_file',
'c2pa_read_ingredient_file',
# Stream
'c2pa_create_stream',
'c2pa_release_stream',
Expand Down Expand Up @@ -645,12 +643,6 @@ def _setup_function(func, argtypes, restype=None):
_setup_function(_lib.c2pa_signer_from_info,
[ctypes.POINTER(C2paSignerInfo)],
ctypes.POINTER(C2paSigner))
_setup_function(
_lib.c2pa_read_file, [
ctypes.c_char_p, ctypes.c_char_p], ctypes.c_void_p)
_setup_function(
_lib.c2pa_read_ingredient_file, [
ctypes.c_char_p, ctypes.c_char_p], ctypes.c_void_p)

# Set up Signer function prototypes
_setup_function(
Expand Down Expand Up @@ -1191,208 +1183,6 @@ def _get_mime_type_from_path(path: Union[str, Path]) -> str:
return mime_type


def read_ingredient_file(
path: Union[str, Path], data_dir: Union[str, Path]) -> str:
"""Read a file as C2PA ingredient (deprecated).
This creates the JSON string that would be used as the ingredient JSON.

.. deprecated:: 0.11.0
This function is deprecated and will be removed in a future version.
To read C2PA metadata, use the :class:`c2pa.c2pa.Reader` class.
To add ingredients to a manifest,
use :meth:`c2pa.c2pa.Builder.add_ingredient` instead.

Args:
path: Path to the file to read
data_dir: Directory to write binary resources to

Returns:
The ingredient as a JSON string

Raises:
C2paError: If there was an error reading the file
"""
warnings.warn(
"The read_ingredient_file function is deprecated and will be "
"removed in a future version. Please use Reader(path).json() for "
"reading C2PA metadata instead, or "
"Builder.add_ingredient(json, format, stream) to add ingredients "
"to a manifest.",
DeprecationWarning,
stacklevel=2,
)

_clear_error_state()

container = _StringContainer()

container._path_str = str(path).encode('utf-8')
container._data_dir_str = str(data_dir).encode('utf-8')

result = _lib.c2pa_read_ingredient_file(
container._path_str, container._data_dir_str)

_check_ffi_operation_result(
result, "Error reading ingredient file {}".format(path))

return _convert_to_py_string(result)


def read_file(path: Union[str, Path],
data_dir: Union[str, Path]) -> str:
"""Read a C2PA manifest from a file (deprecated).

.. deprecated:: 0.10.0
This function is deprecated and will be removed in a future version.
To read C2PA metadata, use the :class:`c2pa.c2pa.Reader` class.

Args:
path: Path to the file to read
data_dir: Directory to write binary resources to

Returns:
The manifest as a JSON string

Raises:
C2paError: If there was an error reading the file
"""
warnings.warn(
"The read_file function is deprecated and will be removed in a "
"future version. Please use the Reader class for reading C2PA "
"metadata instead.",
DeprecationWarning,
stacklevel=2,
)

_clear_error_state()

container = _StringContainer()

container._path_str = str(path).encode('utf-8')
container._data_dir_str = str(data_dir).encode('utf-8')

result = _lib.c2pa_read_file(container._path_str, container._data_dir_str)
_check_ffi_operation_result(
result, "Error during read of manifest from file {}".format(path))

return _convert_to_py_string(result)


@overload
def sign_file(
source_path: Union[str, Path],
dest_path: Union[str, Path],
manifest: str,
signer_info: C2paSignerInfo,
return_manifest_as_bytes: bool = False
) -> Union[str, bytes]:
"""Sign a file with a C2PA manifest using signer info.
"""
...


@overload
def sign_file(
source_path: Union[str, Path],
dest_path: Union[str, Path],
manifest: str,
signer: 'Signer',
return_manifest_as_bytes: bool = False
) -> Union[str, bytes]:
"""Sign a file with a C2PA manifest using a signer.
"""
...


def sign_file(
source_path: Union[str, Path],
dest_path: Union[str, Path],
manifest: str,
signer_or_info: Union[C2paSignerInfo, 'Signer'],
return_manifest_as_bytes: bool = False
) -> Union[str, bytes]:
"""Sign a file with a C2PA manifest (deprecated).
For now, this function is left here to provide a backwards-compatible API.

.. deprecated:: 0.13.0
This function is deprecated and will be removed in a future version.
Use :meth:`Builder.sign` instead.

Args:
source_path: Path to the source file. We will attempt
to guess the mimetype of the source file based on
the extension.
dest_path: Path to write the signed file to
manifest: The manifest JSON string
signer_or_info: Either a signer configuration or a signer object
return_manifest_as_bytes: If True, return manifest bytes instead
of JSON string

Returns:
The signed manifest as a JSON string or bytes, depending
on return_manifest_as_bytes

Raises:
C2paError: If there was an error signing the file
C2paError.Encoding: If any of the string inputs contain
invalid UTF-8 characters
C2paError.NotSupported: If the file type cannot be determined
"""

warnings.warn(
"The sign_file function is deprecated and will be removed in a "
"future version. Please use the Builder object and Builder.sign() "
"instead.",
DeprecationWarning,
stacklevel=2,
)

_clear_error_state()

try:
# Determine if we have a signer or signer info
if isinstance(signer_or_info, C2paSignerInfo):
signer = Signer.from_info(signer_or_info)
own_signer = True
else:
signer = signer_or_info
own_signer = False

# Create a builder from the manifest
builder = Builder(manifest)

manifest_bytes = builder.sign_file(
source_path,
dest_path,
signer
)

if return_manifest_as_bytes:
return manifest_bytes
else:
# Read the signed manifest from the destination file
with Reader(dest_path) as reader:
return reader.json()

except Exception as e:
# Clean up destination file if it exists and there was an error
if os.path.exists(dest_path):
try:
os.remove(dest_path)
except OSError:
logger.warning("Failed to remove destination file")
pass # Ignore cleanup errors

# Re-raise the error
raise C2paError(f"Error signing file: {str(e)}") from e
finally:
# Ensure resources are cleaned up
if 'builder' in locals():
builder.close()
if 'signer' in locals() and own_signer:
signer.close()


class ContextProvider(ABC):
"""Abstract base class for types that provide a C2PA context.

Expand Down Expand Up @@ -3411,52 +3201,6 @@ def add_ingredient_from_stream(
Builder._ERROR_MESSAGES['ingredient_error'].format("Unknown error"),
check=lambda r: r != 0)

def add_ingredient_from_file_path(
self,
ingredient_json: Union[str, dict],
format: str,
filepath: Union[str, Path]):
"""Add an ingredient from a file path to the builder (deprecated).
This is a legacy method.

.. deprecated:: 0.13.0
This method is deprecated and will be removed in a future version.
Use :meth:`add_ingredient` with a file stream instead.

Args:
ingredient_json: The JSON ingredient definition
(either a JSON string or a dictionary)
format: The MIME type or extension of the ingredient
filepath: The path to the file containing the ingredient data
(can be a string or Path object)

Raises:
C2paError: If there was an error adding the ingredient
C2paError.Encoding: If the ingredient JSON or format
contains invalid UTF-8 characters
FileNotFoundError: If the file at the specified path does not exist
"""
warnings.warn(
"add_ingredient_from_file_path is deprecated and will "
"be removed in a future version. Use add_ingredient "
"with a file stream instead.",
DeprecationWarning,
stacklevel=2,
)

try:
# Convert Path object to string if necessary
filepath_str = str(filepath)

# Does the stream handling to use add_ingredient_from_stream
with open(filepath_str, 'rb') as file_stream:
self.add_ingredient_from_stream(
ingredient_json, format, file_stream)
except FileNotFoundError as e:
raise C2paError.FileNotFound(f"File not found: {filepath}") from e
except Exception as e:
raise C2paError.Other(f"Could not add ingredient: {e}") from e

def add_action(self, action_json: Union[str, dict]) -> None:
"""Add an action to the builder, that will be placed
in the actions assertion array in the generated manifest.
Expand Down Expand Up @@ -3991,9 +3735,6 @@ def ed25519_sign(data: bytes, private_key: str) -> bytes:
'Builder',
'Signer',
'load_settings',
'read_file',
'read_ingredient_file',
'sign_file',
'format_embeddable',
'version',
'sdk_version'
Expand Down
Loading
Loading