Skip to content

Commit

Permalink
Importing python bindings works when using conda in windows thanks to…
Browse files Browse the repository at this point in the history
… Joris Vaillant !
  • Loading branch information
SamFlt committed Feb 26, 2024
1 parent 9ce351e commit 0d0ecc1
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 9 deletions.
24 changes: 15 additions & 9 deletions modules/python/bindings/visp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,21 @@
# print(sys.path)


# On windows, we need to explicitely add paths where Python should look for DLLs (This starts with Python >= 3.8)
LOADER_DIR = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))





import _visp
from _visp import *
try:
import _visp
from _visp import *
except ImportError:
import platform
if platform.system() == "Windows": # On windows import can fail because DLLs are not found in the default search paths
from .windows_dll_manager import get_dll_paths, build_directory_manager
# Use the context to clean up the PATH/dll directories after the import (no namespace pollution)
with build_directory_manager() as dll_dir_manager:
for p in get_dll_paths():
dll_dir_manager.add_dll_directory(p)
import _visp
from _visp import *
else:
raise

# Fake module names
for k in _visp.__dict__:
Expand Down
75 changes: 75 additions & 0 deletions modules/python/bindings/visp/windows-dll-manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'''
This code is directly adapted from proxsuite_nlp, see:
- https://github.com/Simple-Robotics/proxsuite-nlp/blob/main/bindings/python/proxsuite_nlp/windows_dll_manager.py
- https://github.com/Simple-Robotics/proxsuite-nlp/blob/main/bindings/python/proxsuite_nlp/__init__.py
On windows, since Python 3.8, dll directories must be explicetly specified (cannot go through path), see
- https://docs.python.org/3/library/os.html#os.add_dll_directory
'''


import os
import sys
import contextlib


def get_dll_paths():
# Assumes that we are in a conda environment, and that ViSP DLLs and the dependencies are installed in this environment
# For the choice of defaults: see https://peps.python.org/pep-0250/#implementation
DEFAULT_DLL_PATHS = [
'..\\..\\..\\..\\bin', # when current folder is lib/python-version/site-packages/package
'..\\..\\..\\bin', # when current folder is lib/site-packages/package
]
# If we have a different setup, the user should specify their own paths
visp_user_defined_dll_paths = os.getenv("VISP_WINDOWS_DLL_PATH")
if visp_user_defined_dll_paths is None:
return [
os.path.join(os.path.dirname(__file__), dll_path) for dll_path in DEFAULT_DLL_PATHS
]
else:
return visp_user_defined_dll_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: # Below 3.8, the path variable is used to search for DLLs
return PathManager()

0 comments on commit 0d0ecc1

Please sign in to comment.