Skip to content

Commit

Permalink
python: Fix unit test running without installing
Browse files Browse the repository at this point in the history
  • Loading branch information
jorisv committed May 19, 2024
1 parent 9219277 commit 365e483
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 0 deletions.
3 changes: 3 additions & 0 deletions python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ SET_TARGET_PROPERTIES(${PYWRAP}
SUFFIX ${PYTHON_EXT_SUFFIX}
OUTPUT_NAME "${PYWRAP}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}"
# On Windows, shared library are treated as binary
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}")
)

IF(UNIX AND NOT APPLE)
Expand All @@ -47,6 +49,7 @@ INSTALL(TARGETS ${PYWRAP} DESTINATION ${${PYWRAP}_INSTALL_DIR})
# --- INSTALL SCRIPTS
SET(PYTHON_FILES
__init__.py
windows_dll_manager.py
)

FOREACH(python ${PYTHON_FILES})
Expand Down
27 changes: 27 additions & 0 deletions python/pycppad/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,30 @@

from .pycppad_pywrap import *
from .pycppad_pywrap import __version__, __raw_version__

# On Windows, if pycppad.dll is not in the same directory than
# the .pyd, it will not be loaded.
# We first try to load pycppad, then, if it fail and we are on Windows:
# 1. We add all paths inside PYCPPAD_WINDOWS_DLL_PATH to DllDirectory
# 2. If PYCPPAD_WINDOWS_DLL_PATH we add the relative path from the
# package directory to the bin directory to DllDirectory
# This solution is inspired from:
# - https://github.com/PixarAnimationStudios/OpenUSD/pull/1511/files
# - https://stackoverflow.com/questions/65334494/python-c-extension-packaging-dll-along-with-pyd
# More resources on https://github.com/diffpy/pyobjcryst/issues/33
try:
from .pycppad_pywrap import *
from .pycppad_pywrap import __version__, __raw_version__
except ImportError:
import platform

if platform.system() == "Windows":
from .windows_dll_manager import get_dll_paths, build_directory_manager

with build_directory_manager() as dll_dir_manager:
for p in get_dll_paths():
dll_dir_manager.add_dll_directory(p)
from .pycppad_pywrap import *
from .pycppad_pywrap import __version__, __raw_version__
else:
raise
56 changes: 56 additions & 0 deletions python/pycppad/windows_dll_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import os
import sys
import contextlib


def get_dll_paths():
pycppad_paths = os.getenv("PYCPPAD_WINDOWS_DLL_PATH")
if pycppad_paths is None:
# Standard site-packages to bin path
RELATIVE_DLL_PATH = "..\\..\\..\\bin"
return [os.path.join(os.path.dirname(__file__), RELATIVE_DLL_PATH)]
else:
return pycppad_paths.split(os.pathsep)


class PathManager(contextlib.AbstractContextManager):
"""Restore PATH state after importing Python module"""

def add_dll_directory(self, dll_dir: str):
os.environ["PATH"] += os.pathsep + dll_dir

def __enter__(self):
self.old_path = os.environ["PATH"]
return self

def __exit__(self, *exc_details):
os.environ["PATH"] = self.old_path


class DllDirectoryManager(contextlib.AbstractContextManager):
"""Restore DllDirectory state after importing Python module"""

def add_dll_directory(self, dll_dir: str):
# add_dll_directory can fail on relative path and non
# existing path.
# Since we don't know all the fail criterion we just ignore
# thrown exception
try:
self.dll_dirs.append(os.add_dll_directory(dll_dir))
except OSError:
pass

def __enter__(self):
self.dll_dirs = []
return self

def __exit__(self, *exc_details):
for d in self.dll_dirs:
d.close()


def build_directory_manager():
if sys.version_info >= (3, 8):
return DllDirectoryManager()
else:
return PathManager()

0 comments on commit 365e483

Please sign in to comment.