mirror of
https://github.com/opencv/opencv.git
synced 2026-09-25 04:09:57 +03:00
Joining BINARIES_PATHS with an unconditional trailing separator creates an empty search-path entry when the variable was unset. On POSIX that empty entry is CWD (ld.so), so children can load the wrong libraries. Keep prepending extras, but only insert the separator when an old value exists. Same join on Windows PATH.
190 lines
6.6 KiB
Python
190 lines
6.6 KiB
Python
'''
|
|
OpenCV Python binary extension loader
|
|
'''
|
|
import os
|
|
import importlib
|
|
import sys
|
|
|
|
__all__ = []
|
|
|
|
try:
|
|
import numpy
|
|
import numpy.core.multiarray
|
|
except ImportError:
|
|
print('OpenCV bindings requires "numpy" package.')
|
|
print('Install it via command:')
|
|
print(' pip install numpy')
|
|
raise
|
|
|
|
# TODO
|
|
# is_x64 = sys.maxsize > 2**32
|
|
|
|
|
|
def _prepend_env_paths(env_key, paths, sep):
|
|
extra = [p for p in paths if p]
|
|
if not extra:
|
|
return
|
|
old = os.environ.get(env_key)
|
|
os.environ[env_key] = sep.join(extra) + ((sep + old) if old else '')
|
|
|
|
|
|
def __load_extra_py_code_for_module(base, name, enable_debug_print=False):
|
|
module_name = "{}.{}".format(__name__, name)
|
|
export_module_name = "{}.{}".format(base, name)
|
|
native_module = sys.modules.pop(module_name, None)
|
|
try:
|
|
py_module = importlib.import_module(module_name)
|
|
except (ImportError, AttributeError) as err:
|
|
if enable_debug_print:
|
|
print("Can't load Python code for module:", module_name,
|
|
". Reason:", err)
|
|
# Extension doesn't contain extra py code
|
|
return False
|
|
|
|
if base in sys.modules and not hasattr(sys.modules[base], name):
|
|
setattr(sys.modules[base], name, py_module)
|
|
sys.modules[export_module_name] = py_module
|
|
# If it is C extension module it is already loaded by cv2 package
|
|
if native_module:
|
|
setattr(py_module, "_native", native_module)
|
|
for k, v in filter(lambda kv: not hasattr(py_module, kv[0]),
|
|
native_module.__dict__.items()):
|
|
if enable_debug_print: print(' symbol({}): {} = {}'.format(name, k, v))
|
|
setattr(py_module, k, v)
|
|
return True
|
|
|
|
|
|
def __collect_extra_submodules(enable_debug_print=False):
|
|
def modules_filter(module):
|
|
return all((
|
|
# module is not internal
|
|
not module.startswith("_"),
|
|
not module.startswith("python-"),
|
|
# it is not a file
|
|
os.path.isdir(os.path.join(_extra_submodules_init_path, module))
|
|
))
|
|
if sys.version_info[0] < 3:
|
|
if enable_debug_print:
|
|
print("Extra submodules is loaded only for Python 3")
|
|
return []
|
|
|
|
__INIT_FILE_PATH = os.path.abspath(__file__)
|
|
_extra_submodules_init_path = os.path.dirname(__INIT_FILE_PATH)
|
|
return filter(modules_filter, os.listdir(_extra_submodules_init_path))
|
|
|
|
|
|
def bootstrap():
|
|
import sys
|
|
|
|
import copy
|
|
save_sys_path = copy.copy(sys.path)
|
|
|
|
if hasattr(sys, 'OpenCV_LOADER'):
|
|
print(sys.path)
|
|
raise ImportError('ERROR: recursion is detected during loading of "cv2" binary extensions. Check OpenCV installation.')
|
|
sys.OpenCV_LOADER = True
|
|
|
|
DEBUG = False
|
|
if hasattr(sys, 'OpenCV_LOADER_DEBUG'):
|
|
DEBUG = True
|
|
|
|
import platform
|
|
if DEBUG: print('OpenCV loader: os.name="{}" platform.system()="{}"'.format(os.name, str(platform.system())))
|
|
|
|
LOADER_DIR = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
|
|
|
|
PYTHON_EXTENSIONS_PATHS = []
|
|
BINARIES_PATHS = []
|
|
|
|
g_vars = globals()
|
|
l_vars = locals().copy()
|
|
|
|
if sys.version_info[:2] < (3, 0):
|
|
from . load_config_py2 import exec_file_wrapper
|
|
else:
|
|
from . load_config_py3 import exec_file_wrapper
|
|
|
|
def load_first_config(fnames, required=True):
|
|
for fname in fnames:
|
|
fpath = os.path.join(LOADER_DIR, fname)
|
|
if not os.path.exists(fpath):
|
|
if DEBUG: print('OpenCV loader: config not found, skip: {}'.format(fpath))
|
|
continue
|
|
if DEBUG: print('OpenCV loader: loading config: {}'.format(fpath))
|
|
exec_file_wrapper(fpath, g_vars, l_vars)
|
|
return True
|
|
if required:
|
|
raise ImportError('OpenCV loader: missing configuration file: {}. Check OpenCV installation.'.format(fnames))
|
|
|
|
load_first_config(['config.py'], True)
|
|
load_first_config([
|
|
'config-{}.{}.py'.format(sys.version_info[0], sys.version_info[1]),
|
|
'config-{}.py'.format(sys.version_info[0])
|
|
], True)
|
|
|
|
if DEBUG: print('OpenCV loader: PYTHON_EXTENSIONS_PATHS={}'.format(str(l_vars['PYTHON_EXTENSIONS_PATHS'])))
|
|
if DEBUG: print('OpenCV loader: BINARIES_PATHS={}'.format(str(l_vars['BINARIES_PATHS'])))
|
|
|
|
applySysPathWorkaround = False
|
|
if hasattr(sys, 'OpenCV_REPLACE_SYS_PATH_0'):
|
|
applySysPathWorkaround = True
|
|
else:
|
|
try:
|
|
BASE_DIR = os.path.dirname(LOADER_DIR)
|
|
if sys.path[0] == BASE_DIR or os.path.realpath(sys.path[0]) == BASE_DIR:
|
|
applySysPathWorkaround = True
|
|
except:
|
|
if DEBUG: print('OpenCV loader: exception during checking workaround for sys.path[0]')
|
|
pass # applySysPathWorkaround is False
|
|
|
|
for p in reversed(l_vars['PYTHON_EXTENSIONS_PATHS']):
|
|
sys.path.insert(1 if not applySysPathWorkaround else 0, p)
|
|
|
|
if os.name == 'nt':
|
|
if sys.version_info[:2] >= (3, 8): # https://github.com/python/cpython/pull/12302
|
|
for p in l_vars['BINARIES_PATHS']:
|
|
try:
|
|
os.add_dll_directory(p)
|
|
except Exception as e:
|
|
if DEBUG: print('Failed os.add_dll_directory(): '+ str(e))
|
|
pass
|
|
_prepend_env_paths('PATH', l_vars['BINARIES_PATHS'], ';')
|
|
if DEBUG: print('OpenCV loader: PATH={}'.format(str(os.environ.get('PATH'))))
|
|
else:
|
|
# amending of LD_LIBRARY_PATH works for sub-processes only
|
|
_prepend_env_paths('LD_LIBRARY_PATH', l_vars['BINARIES_PATHS'], ':')
|
|
|
|
if DEBUG: print("Relink everything from native cv2 module to cv2 package")
|
|
|
|
py_module = sys.modules.pop("cv2")
|
|
|
|
native_module = importlib.import_module("cv2")
|
|
|
|
sys.modules["cv2"] = py_module
|
|
setattr(py_module, "_native", native_module)
|
|
|
|
for item_name, item in filter(lambda kv: kv[0] not in ("__file__", "__loader__", "__spec__",
|
|
"__name__", "__package__"),
|
|
native_module.__dict__.items()):
|
|
if item_name not in g_vars:
|
|
g_vars[item_name] = item
|
|
|
|
sys.path = save_sys_path # multiprocessing should start from bootstrap code (https://github.com/opencv/opencv/issues/18502)
|
|
|
|
try:
|
|
del sys.OpenCV_LOADER
|
|
except Exception as e:
|
|
if DEBUG:
|
|
print("Exception during delete OpenCV_LOADER:", e)
|
|
|
|
if DEBUG: print('OpenCV loader: binary extension... OK')
|
|
|
|
for submodule in __collect_extra_submodules(DEBUG):
|
|
if __load_extra_py_code_for_module("cv2", submodule, DEBUG):
|
|
if DEBUG: print("Extra Python code for", submodule, "is loaded")
|
|
|
|
if DEBUG: print('OpenCV loader: DONE')
|
|
|
|
|
|
bootstrap()
|