From 6c58af9aa40db51f0db1d7b8a2f40f8809f64611 Mon Sep 17 00:00:00 2001 From: Fabian Witt Date: Wed, 11 Nov 2026 13:21:50 +0100 Subject: [PATCH] refactor(build): keep the native library file names in one module The file names of the native library were spelled out in four places: PLATFORM_FOLDERS in setup.py, dynamically_load_library() in lib.py, _lib_name_default in c2pa.py and LIB_NAMES in scripts/build_local_artifacts.py. src/c2pa/_library_names.py now holds them, keyed by sys.platform, and the loader and c2pa.py share its get_library_name(). setup.py and the build script read the mapping with runpy.run_path(), because importing the c2pa package loads the native library. Follow-up to #356. --- scripts/build_local_artifacts.py | 14 +++++++------- setup.py | 18 +++++++++++------- src/c2pa/_library_names.py | 21 +++++++++++++++++++++ src/c2pa/c2pa.py | 8 ++------ src/c2pa/lib.py | 11 +++-------- 5 files changed, 44 insertions(+), 28 deletions(-) create mode 100644 src/c2pa/_library_names.py diff --git a/scripts/build_local_artifacts.py b/scripts/build_local_artifacts.py index 2e833a1d..e1295d73 100644 --- a/scripts/build_local_artifacts.py +++ b/scripts/build_local_artifacts.py @@ -29,6 +29,7 @@ import os import sys +import runpy import shutil import argparse import platform @@ -43,12 +44,11 @@ # Where the package loads the library from at runtime for an editable install. PACKAGE_LIBS_DIR = Path("src/c2pa/libs") -# Library file name per OS (matches what setup.py/lib.py load at runtime). -LIB_NAMES = { - "darwin": "libc2pa_c.dylib", - "linux": "libc2pa_c.so", - "windows": "c2pa_c.dll", -} +# Library file name per sys.platform, loaded by path: importing the c2pa +# package would load the native library. +LIB_NAMES = runpy.run_path( + str(Path(__file__).resolve().parents[1] / "src/c2pa/_library_names.py") +)["LIBRARY_NAMES"] def get_platform_identifier(): @@ -180,7 +180,7 @@ def build_native(c2pa_rs_path, debug=False): """Build the FFI crate for the host arch. Returns the built library path.""" profile = "debug" if debug else "release" run_cargo(c2pa_rs_path, debug=debug) - lib_name = LIB_NAMES[platform.system().lower()] + lib_name = LIB_NAMES[sys.platform] lib = c2pa_rs_path / "target" / profile / lib_name if not lib.is_file(): print(f"Error: expected built library not found: {lib}") diff --git a/setup.py b/setup.py index e90bb79e..d4169960 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ from setuptools import setup, find_namespace_packages import sys import platform +import runpy import shutil from pathlib import Path import toml @@ -26,16 +27,19 @@ def get_version(): VERSION = get_version() PACKAGE_NAME = "c2pa-python" # Define package name as a constant +# Loaded by path: importing the c2pa package would load the native library +LIBRARY_NAMES = runpy.run_path("src/c2pa/_library_names.py")["LIBRARY_NAMES"] + # Based on what c2pa-rs repo publishes: each platform folder and the # native library in it that src/c2pa/lib.py loads PLATFORM_FOLDERS = { - 'universal-apple-darwin': 'libc2pa_c.dylib', - 'aarch64-apple-darwin': 'libc2pa_c.dylib', - 'x86_64-apple-darwin': 'libc2pa_c.dylib', - 'x86_64-pc-windows-msvc': 'c2pa_c.dll', - 'aarch64-pc-windows-msvc': 'c2pa_c.dll', - 'x86_64-unknown-linux-gnu': 'libc2pa_c.so', - 'aarch64-unknown-linux-gnu': 'libc2pa_c.so', + 'universal-apple-darwin': LIBRARY_NAMES['darwin'], + 'aarch64-apple-darwin': LIBRARY_NAMES['darwin'], + 'x86_64-apple-darwin': LIBRARY_NAMES['darwin'], + 'x86_64-pc-windows-msvc': LIBRARY_NAMES['win32'], + 'aarch64-pc-windows-msvc': LIBRARY_NAMES['win32'], + 'x86_64-unknown-linux-gnu': LIBRARY_NAMES['linux'], + 'aarch64-unknown-linux-gnu': LIBRARY_NAMES['linux'], } # Directory structure diff --git a/src/c2pa/_library_names.py b/src/c2pa/_library_names.py new file mode 100644 index 00000000..76a35e78 --- /dev/null +++ b/src/c2pa/_library_names.py @@ -0,0 +1,21 @@ +"""File names of the native c2pa library, keyed by sys.platform. + +Standard library only: setup.py and scripts/build_local_artifacts.py load +this file by path, because importing the c2pa package loads the library. +""" + +import sys + +LIBRARY_NAMES = { + "darwin": "libc2pa_c.dylib", + "linux": "libc2pa_c.so", + "win32": "c2pa_c.dll", +} + + +def get_library_name() -> str: + """Return the native library file name for the running platform.""" + library_name = LIBRARY_NAMES.get(sys.platform) + if library_name is None: + raise RuntimeError(f"Unsupported platform: {sys.platform}") + return library_name diff --git a/src/c2pa/c2pa.py b/src/c2pa/c2pa.py index a9090cc1..7f3863d6 100644 --- a/src/c2pa/c2pa.py +++ b/src/c2pa/c2pa.py @@ -29,6 +29,7 @@ from typing import Optional, Union, Callable, Any, overload import io from .lib import dynamically_load_library, record_owner_pid, is_foreign_process +from ._library_names import get_library_name import mimetypes from itertools import count @@ -144,12 +145,7 @@ def _validate_library_exports(lib): # Determine the library name based on the platform -if sys.platform == "win32": # pragma: no cover - _lib_name_default = "c2pa_c.dll" -elif sys.platform == "darwin": # pragma: no cover - _lib_name_default = "libc2pa_c.dylib" -else: # pragma: no cover - _lib_name_default = "libc2pa_c.so" +_lib_name_default = get_library_name() # Check for C2PA_LIBRARY_NAME environment variable env_lib_name = os.environ.get("C2PA_LIBRARY_NAME") diff --git a/src/c2pa/lib.py b/src/c2pa/lib.py index be6353f8..6848353d 100644 --- a/src/c2pa/lib.py +++ b/src/c2pa/lib.py @@ -13,6 +13,8 @@ from typing import Optional from enum import Enum +from ._library_names import get_library_name + # Debug flag for library loading DEBUG_LIBRARY_LOADING = False @@ -219,14 +221,7 @@ def dynamically_load_library( Returns: The loaded library or None if loading failed """ - if sys.platform == "darwin": - c2pa_lib_name = "libc2pa_c.dylib" - elif sys.platform == "linux": - c2pa_lib_name = "libc2pa_c.so" - elif sys.platform == "win32": - c2pa_lib_name = "c2pa_c.dll" - else: - raise RuntimeError(f"Unsupported platform: {sys.platform}") + c2pa_lib_name = get_library_name() if DEBUG_LIBRARY_LOADING: # pragma: no cover logger.info(f"Current working directory: {Path.cwd()}")