Commit 10033dd6 authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

better native libs (libvips) management on macos

parent 353ffb2b
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
import ctypes.util
import os
import sys
from pathlib import Path
from shutil import which


def _prepend_env_path(name: str, path: Path) -> None:
    path_value = str(path)
    existing = os.environ.get(name)
    if existing:
        paths = existing.split(os.pathsep)
        if path_value in paths:
            return
        os.environ[name] = os.pathsep.join([path_value, *paths])
        return
    os.environ[name] = path_value


def _homebrew_library_dirs() -> list[Path]:
    prefixes: list[Path] = []
    vips_executable = which("vips")
    if vips_executable:
        prefixes.append(Path(vips_executable).resolve().parent.parent)
    prefixes.extend([Path("/opt/homebrew"), Path("/usr/local")])

    lib_dirs: list[Path] = []
    for prefix in prefixes:
        lib_dir = prefix / "lib"
        if lib_dir not in lib_dirs and (lib_dir / "libvips.42.dylib").exists():
            lib_dirs.append(lib_dir)
    return lib_dirs


def configure_macos_homebrew_library_paths() -> None:
    if sys.platform != "darwin" or ctypes.util.find_library("vips"):
        return
    for lib_dir in _homebrew_library_dirs():
        _prepend_env_path("DYLD_LIBRARY_PATH", lib_dir)
        _prepend_env_path("DYLD_FALLBACK_LIBRARY_PATH", lib_dir)


configure_macos_homebrew_library_paths()
+20 −1
Original line number Diff line number Diff line
import os
from django.core.management.base import BaseCommand, CommandError
from django.db.models import OuterRef, Subquery
from fuse import FUSE, FuseOSError, Operations
from dataclasses import dataclass
import errno
import stat
from resources.models import Collection, FileExtension, slugify, File
from jama import settings

try:
    from fuse import FUSE, FuseOSError, Operations
except OSError as exc:
    FUSE = None
    FUSE_IMPORT_ERROR = exc

    class FuseOSError(OSError):
        def __init__(self, errno):
            super().__init__(errno, os.strerror(errno))

    class Operations:
        def __call__(self, op, *args):
            if not hasattr(self, op):
                raise FuseOSError(errno.EFAULT)
            return getattr(self, op)(*args)
else:
    FUSE_IMPORT_ERROR = None


STAT_KEYS = (
    "st_atime",
@@ -376,6 +393,8 @@ class Command(BaseCommand):
        ).first()
        if not root_collection:
            raise CommandError(f"{root_collection_id} is not a valid collection id")
        if FUSE is None:
            raise CommandError("Unable to load libfuse") from FUSE_IMPORT_ERROR
        FUSE(
            Ops(root_collection),
            mount_point,
+9 −0
Original line number Diff line number Diff line
@@ -421,6 +421,15 @@ class FuseFsOpsTestCase(TestCase):

        fuse.assert_not_called()

    def test_command_reports_missing_libfuse(self):
        with TemporaryDirectory() as mount_point:
            with (
                patch.object(fusefs, "FUSE", None),
                patch.object(fusefs, "FUSE_IMPORT_ERROR", OSError("missing fuse")),
                self.assertRaises(CommandError),
            ):
                call_command("fusefs", mount_point, self.root.pk)


class ProjectSnapshotTestCase(TestCase):
    def setUp(self):
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from django.http import (
    FileResponse,
)
from django.contrib.auth.decorators import login_required
import jama.native_libs  # noqa: F401 - configures libvips lookup on macOS
import pyvips
from resources.models import File
from resources.acl import UserAccess
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from glob import glob as _glob
from typing import List, Dict, Iterator, Union, Tuple, Optional
import cv2
import numpy
import jama.native_libs  # noqa: F401 - configures libvips lookup on macOS
import pyvips
import unidecode
from django.contrib.auth.models import User