From e9b5b9cbd3bc4afa93a63097973bf7642e34c29d Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 15:23:35 +0100 Subject: [PATCH 1/9] BUG: fix install for OTB 9 --- pyotb/install.py | 106 +++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 64 deletions(-) diff --git a/pyotb/install.py b/pyotb/install.py index 0cf139e..814f1dc 100644 --- a/pyotb/install.py +++ b/pyotb/install.py @@ -1,4 +1,5 @@ """This module contains functions for interactive auto installation of OTB.""" + from __future__ import annotations import json @@ -19,8 +20,7 @@ def interactive_config(): version = input("Choose a version to download (default is latest): ") default_dir = Path.home() / "Applications" path = input(f"Parent directory for installation (default is {default_dir}): ") - env = input("Permanently change user's environment variables ? (y/n): ") == "y" - return version, path, env + return version, path def otb_latest_release_tag(): @@ -35,7 +35,7 @@ def otb_latest_release_tag(): return releases[-1] -def check_versions(sysname: str, python_minor: int, otb_major: int) -> tuple[bool, int]: +def check_versions(python_minor: int, otb_major: int) -> tuple[bool, int]: """Verify if python version is compatible with major OTB version. Args: @@ -47,56 +47,22 @@ def check_versions(sysname: str, python_minor: int, otb_major: int) -> tuple[boo (True, 0) if compatible or (False, expected_version) in case of conflicts """ - if sysname == "Win64": + if sys.platform == "win32": expected = 5 if otb_major in (6, 7) else 7 if python_minor == expected: return True, 0 - elif sysname == "Darwin64": + elif sys.platform == "darwin": expected = 7, 0 if python_minor == expected: return True, 0 - elif sysname == "Linux64": + elif sys.platform == "linux": expected = 5 if otb_major in (6, 7) else 8 if python_minor == expected: return True, 0 return False, expected -def env_config_unix(otb_path: Path): - """Update env profile for current user with new otb_env.profile call. - - Args: - otb_path: the path of the new OTB installation - - """ - profile = Path.home() / ".profile" - with profile.open("a", encoding="utf-8") as buf: - buf.write(f'\n. "{otb_path}/otbenv.profile"\n') - print(f"##### Added new environment variables to {profile}") - - -def env_config_windows(otb_path: Path): - """Update user's registry hive with new OTB_ROOT env variable. - - Args: - otb_path: path of the new OTB installation - - """ - import winreg # pylint: disable=import-error,import-outside-toplevel - - with winreg.OpenKeyEx( - winreg.HKEY_CURRENT_USER, "Environment", 0, winreg.KEY_SET_VALUE - ) as reg_key: - winreg.SetValueEx(reg_key, "OTB_ROOT", 0, winreg.REG_EXPAND_SZ, str(otb_path)) - print( - "##### Environment variable 'OTB_ROOT' added to user's registry. " - "You'll need to login / logout to apply this change." - ) - reg_cmd = "reg.exe delete HKEY_CURRENT_USER\\Environment /v OTB_ROOT /f" - print(f"To undo this, you may use '{reg_cmd}'") - - -def install_otb(version: str = "latest", path: str = "", edit_env: bool = True): +def install_otb(version: str = "latest", path: str = ""): """Install pre-compiled OTB binaries in path, use latest release by default. Args: @@ -118,13 +84,24 @@ def install_otb(version: str = "latest", path: str = "", edit_env: bool = True): python_minor = sys.version_info.minor if not version or version == "latest": version = otb_latest_release_tag() + otb_major = int(version[0]) name_corresp = {"linux": "Linux64", "darwin": "Darwin64", "win32": "Win64"} + if sys.platform == "win32": + ext = "zip" + if otb_major < 9: + name_corresp = {"win32": "Win64"} + else: + name_corresp = {"win32": "win64"} + elif otb_major < 9: + ext = "run" + name_corresp["linux"] = "Linux64" + else: + ext = "tar.gz" + name_corresp["linux"] = "Linux" sysname = name_corresp[sys.platform] - ext = "zip" if sysname == "Win64" else "run" - cmd = which("zsh") or which("bash") or which("sh") - otb_major = int(version[0]) - check, expected = check_versions(sysname, python_minor, otb_major) - if sysname == "Win64" and not check: + + check, expected = check_versions(python_minor, otb_major) + if sys.platform == "win32" and not check: raise SystemExit( f"Python 3.{expected} is required to import bindings on Windows." ) @@ -141,30 +118,32 @@ def install_otb(version: str = "latest", path: str = "", edit_env: bool = True): path = Path(path) else: default_path = True - path = Path.home() / "Applications" / tmpfile.stem - if sysname == "Win64": + path = Path.home() / "Applications" + + if sys.platform == "win32": with zipfile.ZipFile(tmpfile) as zipf: print("##### Extracting zip file") # Unzip will always create a dir with OTB-version name - zipf.extractall(path.parent if default_path else path) - else: + zipf.extractall(path) + elif otb_major < 9: + if default_path: + path = path / ('OTB-' + version) + cmd = which("zsh") or which("bash") or which("sh") install_cmd = f"{cmd} {tmpfile} --target {path} --accept" print(f"##### Executing '{install_cmd}'\n") subprocess.run(install_cmd, shell=True, check=True) + else: + path = path / ('OTB-' + version) + path.mkdir(parents=True, exist_ok = True) + install_cmd = f"tar -xvf {tmpfile} -C {path}" + print(f"##### Executing '{install_cmd}'\n") + subprocess.run(install_cmd, shell=True, check=True) + tmpfile.unlink() # cleaning - # Add env variable to profile - if edit_env: - if sysname == "Win64": - env_config_windows(path) - else: - env_config_unix(path) - elif not default_path: - ext = "bat" if sysname == "Win64" else "profile" - print( - f"Remember to call '{path}{os.sep}otbenv.{ext}' before importing pyotb, " - f"or add 'OTB_ROOT=\"{path}\"' to your env variables." - ) + if not default_path: + ext = "bat" if sys.platform == "win32" else "profile" + print(f"Remember to call '{path}{os.sep}otbenv.{ext}' before importing pyotb.") # Requirements are met, no recompilation or symlink required if check: return str(path) @@ -179,14 +158,13 @@ def install_otb(version: str = "latest", path: str = "", edit_env: bool = True): try: print("\n!!!!! Python version mismatch, trying to recompile bindings") ctest_cmd = ( - ". ./otbenv.profile && ctest -S share/otb/swig/build_wrapping.cmake -V" + "bash -c 'source otbenv.profile && ctest -S share/otb/swig/build_wrapping.cmake -V'" ) print(f"##### Executing '{ctest_cmd}'") subprocess.run(ctest_cmd, cwd=path, check=True, shell=True) return str(path) except subprocess.CalledProcessError: print("\nCompilation failed.") - # TODO: support for sudo auto build deps install using apt, pacman/yay, brew... print( "You need cmake, python3-dev and libgl1-mesa-dev installed." "\nTrying to symlink libraries instead - this may fail with newest versions." -- GitLab From 41a0870455068dfcd9faa909ab3563875d73ffc3 Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 15:36:06 +0100 Subject: [PATCH 2/9] BUG: remove auto env setting functions because it's broken --- pyotb/helpers.py | 324 +++-------------------------------------------- 1 file changed, 15 insertions(+), 309 deletions(-) diff --git a/pyotb/helpers.py b/pyotb/helpers.py index 0e6ea2a..6449f70 100644 --- a/pyotb/helpers.py +++ b/pyotb/helpers.py @@ -1,16 +1,11 @@ """This module ensure we properly initialize pyotb, or raise SystemExit in case of broken install.""" + import logging import os import sys -import sysconfig -from pathlib import Path -from shutil import which -from .install import install_otb, interactive_config +from .install import interactive_config, install_otb -# Allow user to switch between OTB directories without setting every env variable -OTB_ROOT = os.environ.get("OTB_ROOT") -DOCS_URL = "https://www.orfeo-toolbox.org/CookBook/Installation.html" # Logging # User can also get logger with `logging.getLogger("pyOTB")` @@ -42,312 +37,23 @@ def set_logger_level(level: str): logger_handler.setLevel(getattr(logging, level)) -def find_otb(prefix: str = OTB_ROOT, scan: bool = True): - """Try to load OTB bindings or scan system, help user in case of failure, set env. - - If in interactive prompt, user will be asked if he wants to install OTB. - The OTB_ROOT variable allow one to override default OTB version, with auto env setting. - Path precedence : $OTB_ROOT > location of python bindings location - Then, if OTB is not found: - search for releases installations: $HOME/Applications - OR (for Linux): /opt/otbtf > /opt/otb > /usr/local > /usr - OR (for Windows): C:/Program Files - - Args: - prefix: prefix to search OTB in (Default value = OTB_ROOT) - scan: find otb in system known locations (Default value = True) - - Returns: - otbApplication module - - Raises: - SystemError: is OTB is not found (when using interactive mode) - SystemExit: if OTB is not found, since pyotb won't be usable - - """ - otb = None - # Try OTB_ROOT env variable first (allow override default OTB version) - if prefix: - try: - set_environment(prefix) - import otbApplication as otb # pylint: disable=import-outside-toplevel - - return otb - except SystemError as e: - raise SystemExit(f"Failed to import OTB with prefix={prefix}") from e - except ImportError as e: - __suggest_fix_import(str(e), prefix) - raise SystemExit("Failed to import OTB. Exiting.") from e - # Else try import from actual Python path +def try_import(): + """Try to load OTB bindings or scan system, help user in case of failure.""" try: - # Here, we can't properly set env variables before OTB import. - # We assume user did this before running python - # For LD_LIBRARY_PATH problems, use OTB_ROOT instead of PYTHONPATH import otbApplication as otb # pylint: disable=import-outside-toplevel - - if "OTB_APPLICATION_PATH" not in os.environ: - lib_dir = __find_lib(otb_module=otb) - apps_path = __find_apps_path(lib_dir) - otb.Registry.SetApplicationPath(apps_path) - return otb - except ImportError as e: - pythonpath = os.environ.get("PYTHONPATH") - if not scan: - raise SystemExit( - f"Failed to import OTB with env PYTHONPATH={pythonpath}" - ) from e - # Else search system - logger.info("Failed to import OTB. Searching for it...") - prefix = __find_otb_root() - # Try auto install if shell is interactive - if not prefix and hasattr(sys, "ps1"): - if input("OTB is missing. Do you want to install it ? (y/n): ") == "y": - return find_otb(install_otb(*interactive_config())) - raise SystemError("OTB libraries not found on disk. ") - if not prefix: - raise SystemExit( - "OTB libraries not found on disk. " - "To install it, open an interactive python shell and 'import pyotb'" - ) - # If OTB was found on disk, set env and try to import one last time - try: - set_environment(prefix) - import otbApplication as otb # pylint: disable=import-outside-toplevel - - return otb - except SystemError as e: - raise SystemExit("Auto setup for OTB env failed. Exiting.") from e - # Help user to fix this - except ImportError as e: - __suggest_fix_import(str(e), prefix) - raise SystemExit("Failed to import OTB. Exiting.") from e - - -def set_environment(prefix: str): - """Set environment variables (before OTB import), raise error if anything is wrong. - - Args: - prefix: path to OTB root directory - - Raises: - SystemError: if OTB or GDAL is not found - - """ - logger.info("Preparing environment for OTB in %s", prefix) - # OTB root directory - prefix = Path(prefix) - if not prefix.exists(): - raise FileNotFoundError(str(prefix)) - built_from_source = False - if not (prefix / "README").exists(): - built_from_source = True - # External libraries - lib_dir = __find_lib(prefix) - if not lib_dir: - raise SystemError("Can't find OTB external libraries") - # LD library path : this does not seems to work - if sys.platform == "linux" and built_from_source: - new_ld_path = f"{lib_dir}:{os.environ.get('LD_LIBRARY_PATH') or ''}" - os.environ["LD_LIBRARY_PATH"] = new_ld_path - - # Add python bindings directory first in PYTHONPATH - otb_api = __find_python_api(lib_dir) - if not otb_api: - raise SystemError("Can't find OTB Python API") - if otb_api not in sys.path: - sys.path.insert(0, otb_api) - - # Add /bin first in PATH, in order to avoid conflicts with another GDAL install - os.environ["PATH"] = f"{prefix / 'bin'}{os.pathsep}{os.environ['PATH']}" - # Ensure APPLICATION_PATH is set - apps_path = __find_apps_path(lib_dir) - if Path(apps_path).exists(): - os.environ["OTB_APPLICATION_PATH"] = apps_path - else: - raise SystemError("Can't find OTB applications directory") - os.environ["LC_NUMERIC"] = "C" - os.environ["GDAL_DRIVER_PATH"] = "disable" - - # Find GDAL libs - if (prefix / "share/gdal").exists(): - # Local GDAL (OTB Superbuild, .run, .exe) - gdal_data = str(prefix / "share/gdal") - proj_lib = str(prefix / "share/proj") - elif sys.platform == "linux": - # If installed using apt or built from source with system deps - gdal_data = "/usr/share/gdal" - proj_lib = "/usr/share/proj" - elif sys.platform == "win32": - gdal_data = str(prefix / "share/data") - proj_lib = str(prefix / "share/proj") - else: - raise SystemError( - f"Can't find GDAL location with current OTB prefix '{prefix}' or in /usr" - ) - os.environ["GDAL_DATA"] = gdal_data - os.environ["PROJ_LIB"] = proj_lib - - -def __find_lib(prefix: str = None, otb_module=None): - """Try to find OTB external libraries directory. - - Args: - prefix: try with OTB root directory - otb_module: try with otbApplication library path if found, else None - - Returns: - lib path, or None if not found - - """ - if prefix is not None: - lib_dir = prefix / "lib" - if lib_dir.exists(): - return lib_dir.absolute() - if otb_module is not None: - lib_dir = Path(otb_module.__file__).parent.parent - # Case OTB .run file - if lib_dir.name == "lib": - return lib_dir.absolute() - # Case /usr - lib_dir = lib_dir.parent - if lib_dir.name in ("lib", "x86_64-linux-gnu"): - return lib_dir.absolute() - # Case built from source (/usr/local, /opt/otb, ...) - lib_dir = lib_dir.parent - if lib_dir.name == "lib": - return lib_dir.absolute() - return None - - -def __find_python_api(lib_dir: Path): - """Try to find the python path. - - Args: - prefix: prefix - - Returns: - OTB python API path, or None if not found - - """ - otb_api = lib_dir / "python" - if not otb_api.exists(): - otb_api = lib_dir / "otb/python" - if otb_api.exists(): - return str(otb_api.absolute()) - logger.debug("Failed to find OTB python bindings directory") - return None - - -def __find_apps_path(lib_dir: Path): - """Try to find the OTB applications path. - - Args: - lib_dir: library path - - Returns: - application path, or empty string if not found - - """ - if lib_dir.exists(): - otb_application_path = lib_dir / "otb/applications" - if otb_application_path.exists(): - return str(otb_application_path.absolute()) - # This should not happen, may be with failed builds ? - logger.error("Library directory found but 'applications' is missing") - return "" - - -def __find_otb_root(): - """Search for OTB root directory in well known locations. - - Returns: - str path of the OTB directory, or None if not found - - """ - prefix = None - # Search possible known locations (system scpecific) - if sys.platform == "linux": - possible_locations = ( - "/usr/lib/x86_64-linux-gnu/otb", - "/usr/local/lib/otb/", - "/opt/otb/lib/otb/", - "/opt/otbtf/lib/otb", - ) - for str_path in possible_locations: - path = Path(str_path) - if not path.exists(): - continue - logger.info("Found %s", str_path) - if path.parent.name == "x86_64-linux-gnu": - prefix = path.parent.parent.parent - else: - prefix = path.parent.parent - elif sys.platform == "win32": - for path in sorted(Path("c:/Program Files").glob("**/OTB-*/lib")): - logger.info("Found %s", path.parent) - prefix = path.parent - # Search for pyotb OTB install, or default on macOS - apps = Path.home() / "Applications" - for path in sorted(apps.glob("OTB-*/lib/")): - logger.info("Found %s", path.parent) - prefix = path.parent - # Return latest found prefix (and version), see precedence in find_otb() docstrings - if isinstance(prefix, Path): - return prefix.absolute() - return None - - -def __suggest_fix_import(error_message: str, prefix: str): - """Help user to fix the OTB installation with appropriate log messages.""" - logger.critical("An error occurred while importing OTB Python API") - logger.critical("OTB error message was '%s'", error_message) - if sys.platform == "win32": - if error_message.startswith("DLL load failed"): - if sys.version_info.minor != 7: - logger.critical( - "You need Python 3.5 (OTB 6.4 to 7.4) or Python 3.7 (since OTB 8)" - ) - else: - logger.critical( - "It seems that your env variables aren't properly set," - " first use 'call otbenv.bat' then try to import pyotb once again" + except ModuleNotFoundError as e: + if hasattr(sys, "ps1"): + if input("Cannot import OTB. Do you want to install it ? (y/n): ") == "y": + install_otb(*interactive_config()) + logger.warning( + "You need to restart the python process after calling 'source otbenv.profile'" ) - elif error_message.startswith("libpython3."): - logger.critical( - "It seems like you need to symlink or recompile python bindings" - ) - if ( - sys.executable.startswith("/usr/bin") - and which("ctest") - and which("python3-config") - ): - logger.critical( - "To compile, use 'cd %s ; source otbenv.profile ; " - "ctest -S share/otb/swig/build_wrapping.cmake -VV'", - prefix, - ) - return - logger.critical( - "You may need to install cmake, python3-dev and mesa's libgl" - " in order to recompile python bindings" - ) - expected = int(error_message[11]) - if expected != sys.version_info.minor: - logger.critical( - "Python library version mismatch (OTB expected 3.%s) : " - "a symlink may not work, depending on your python version", - expected, - ) - lib_dir = sysconfig.get_config_var("LIBDIR") - lib = f"{lib_dir}/libpython3.{sys.version_info.minor}.so" - if Path(lib).exists(): - target = f"{prefix}/lib/libpython3.{expected}.so.1.0" - logger.critical("If using OTB>=8.0, try 'ln -sf %s %s'", lib, target) - logger.critical( - "You can verify installation requirements for your OS at %s", DOCS_URL - ) + raise SystemExit( + "Unable to import OTB. You may need to run 'source otbenv.profile'. " + "If you need to install it, use 'import pyotb' from an interactive python." + ) from e # This part of pyotb is the first imported during __init__ and checks if OTB is found # If OTB isn't found, a SystemExit is raised to prevent execution of the core module -find_otb() +try_import() -- GitLab From bd1b8352c0bed9ff8acac038e828f867569c8cb6 Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 15:38:52 +0100 Subject: [PATCH 3/9] ENH: better logging config using dict --- pyotb/helpers.py | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/pyotb/helpers.py b/pyotb/helpers.py index 6449f70..829cb79 100644 --- a/pyotb/helpers.py +++ b/pyotb/helpers.py @@ -8,33 +8,36 @@ from .install import interactive_config, install_otb # Logging -# User can also get logger with `logging.getLogger("pyOTB")` +# User can also get logger with `logging.getLogger("pyotb")` # then use pyotb.set_logger_level() to adjust logger verbosity -logger = logging.getLogger("pyotb") -logger_handler = logging.StreamHandler(sys.stdout) -formatter = logging.Formatter( - fmt="%(asctime)s (%(levelname)-4s) [pyotb] %(message)s", datefmt="%Y-%m-%d %H:%M:%S" -) -logger_handler.setFormatter(formatter) + # Search for PYOTB_LOGGER_LEVEL, else use OTB_LOGGER_LEVEL as pyotb level, or fallback to INFO LOG_LEVEL = ( os.environ.get("PYOTB_LOGGER_LEVEL") or os.environ.get("OTB_LOGGER_LEVEL") or "INFO" ) -logger.setLevel(getattr(logging, LOG_LEVEL)) -# Here it would be possible to use a different level for a specific handler -# A more verbose one can go to text file while print only errors to stdout -logger_handler.setLevel(getattr(logging, LOG_LEVEL)) -logger.addHandler(logger_handler) +logger = logging.getLogger("pyotb") -def set_logger_level(level: str): - """Allow user to change the current logging level. - - Args: - level: logging level string ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') - """ - logger.setLevel(getattr(logging, level)) - logger_handler.setLevel(getattr(logging, level)) +logging_cfg = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "default": { + "format": "%(asctime)s (%(levelname)-4s) [pyotb] %(message)s", + "datefmt": "%Y-%m-%d %H:%M:%S", + }, + }, + "handlers": { + "stdout": { + "class": "logging.StreamHandler", + "level": LOG_LEVEL, + "formatter": "default", + "stream": "ext://sys.stdout", + } + }, + "loggers": {"pyotb": {"level": LOG_LEVEL, "handlers": ["stdout"]}}, +} +logging.config.dictConfig(logging_cfg) def try_import(): -- GitLab From bc7470be933fa913f160e0aa136f1c57bc70f6e1 Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 15:48:27 +0100 Subject: [PATCH 4/9] DOC: remove outdated pages --- doc/index.md | 3 +- doc/managing_loggers.md | 10 +++--- doc/otb_versions.md | 69 ----------------------------------------- 3 files changed, 5 insertions(+), 77 deletions(-) delete mode 100644 doc/otb_versions.md diff --git a/doc/index.md b/doc/index.md index 4ecfaf4..87155fc 100644 --- a/doc/index.md +++ b/doc/index.md @@ -23,7 +23,6 @@ to make OTB more Python friendly. - [Comparison between pyotb and OTB native library](comparison_otb.md) - [Summarize applications](summarize.md) -- [OTB versions](otb_versions.md) - [Managing loggers](managing_loggers.md) - [Troubleshooting & limitations](troubleshooting.md) @@ -40,4 +39,4 @@ Open a PR/MR, or file an issue if you spot a bug or have any suggestion: - [Github](https://github.com/orfeotoolbox/pyotb) - [Orfeo ToolBox GitLab instance](https://gitlab.orfeo-toolbox.org/nicolasnn/pyotb). -Thank you! \ No newline at end of file +Thank you! diff --git a/doc/managing_loggers.md b/doc/managing_loggers.md index 7220af4..3b2d37d 100644 --- a/doc/managing_loggers.md +++ b/doc/managing_loggers.md @@ -3,19 +3,17 @@ Several environment variables are used in order to adjust logger level and behaviour. It should be set before importing pyotb. -- `OTB_LOGGER_LEVEL` : used to set the default OTB logger level. -- `PYOTB_LOGGER_LEVEL` : used to set the pyotb logger level. if not set, -- `OTB_LOGGER_LEVEL` will be used. +- `OTB_LOGGER_LEVEL` : used to set the default OTB logger level +- `PYOTB_LOGGER_LEVEL` : used to set the pyotb logger level (else OTB level is used) If none of those two variables is set, the logger level will be set to 'INFO'. Available levels are : DEBUG, INFO, WARNING, ERROR, CRITICAL -You may also change the logger level after import (for pyotb only) with the -function `set_logger_level`. +You may also change the logger level after import (for pyotb only) : ```python import pyotb -pyotb.set_logger_level('DEBUG') +pyotb.logger.setLevel('DEBUG') ``` Bonus : in some cases, you may want to silence the GDAL driver logger (for diff --git a/doc/otb_versions.md b/doc/otb_versions.md deleted file mode 100644 index aa8889d..0000000 --- a/doc/otb_versions.md +++ /dev/null @@ -1,69 +0,0 @@ -## System with multiple OTB versions - -If you want to quickly switch between OTB versions, or override the default -system version, you may use the `OTB_ROOT` env variable : - -```python -import os -# This is equivalent to "[set/export] OTB_ROOT=/opt/otb" before launching python -os.environ['OTB_ROOT'] = '/opt/otb' -import pyotb -``` - -```text -2022-06-14 13:59:03 (INFO) [pyOTB] Preparing environment for OTB in /opt/otb -2022-06-14 13:59:04 (INFO) [pyOTB] Successfully loaded 126 OTB applications -``` - -If you try to import pyotb without having set environment, it will try to find -any OTB version installed on your system: - -```python -import pyotb -``` - -```text -2022-06-14 13:55:41 (INFO) [pyOTB] Failed to import OTB. Searching for it... -2022-06-14 13:55:41 (INFO) [pyOTB] Found /opt/otb/lib/otb/ -2022-06-14 13:55:41 (INFO) [pyOTB] Found /opt/otbtf/lib/otb -2022-06-14 13:55:42 (INFO) [pyOTB] Found /home/otbuser/Applications/OTB-8.0.1-Linux64 -2022-06-14 13:55:43 (INFO) [pyOTB] Preparing environment for OTB in /home/otbuser/Applications/OTB-8.0.1-Linux64 -2022-06-14 13:55:44 (INFO) [pyOTB] Successfully loaded 117 OTB applications -``` - -Here is the path precedence for this automatic env configuration : - -```text - OTB_ROOT env variable > python bindings directory - OR search for releases installations : HOME - OR (for linux) : /opt/otbtf > /opt/otb > /usr/local > /usr - OR (for windows) : C:/Program Files -``` - -!!! Note - - When `otbApplication` is found in `PYTHONPATH` (and `OTB_ROOT` not set), - the OTB installation where the python API is linked, will be used. - -## Fresh OTB installation - -If you've just installed OTB binaries in a Linux environment, you may -encounter an error at first import, pyotb will help you fix it : - -```python -import pyotb -``` - -```text -2022-06-14 14:00:34 (INFO) [pyOTB] Preparing environment for OTB in /home/otbuser/Applications/OTB-8.0.1-Linux64 -2022-07-07 16:56:04 (CRITICAL) [pyOTB] An error occurred while importing OTB Python API -2022-07-07 16:56:04 (CRITICAL) [pyOTB] OTB error message was 'libpython3.8.so.rh-python38-1.0: cannot open shared object file: No such file or directory' -2022-07-07 16:56:04 (CRITICAL) [pyOTB] It seems like you need to symlink or recompile python bindings -2022-07-07 16:56:04 (CRITICAL) [pyOTB] Use 'ln -s /usr/lib/x86_64-linux-gnu/libpython3.8.so /home/otbuser/Applications/OTB-8.0.1-Linux64/lib/libpython3.8.so.rh-python38-1.0' - -# OR in case Python version is not 3.8 and cmake is installed : -2022-07-07 16:54:34 (CRITICAL) [pyOTB] Python library version mismatch (OTB was expecting 3.8) : a simple symlink may not work, depending on your python version -2022-07-07 16:54:34 (CRITICAL) [pyOTB] To recompile python bindings, use 'cd /home/otbuser/Applications/OTB-8.0.1-Linux64 ; source otbenv.profile ; ctest -S share/otb/swig/build_wrapping.cmake -VV' - -Failed to import OTB. Exiting. -``` -- GitLab From d1e1a4efe5d259b5dd5460d44ba7082e9ab14288 Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 15:49:36 +0100 Subject: [PATCH 5/9] CI: bump to version 2.0.2 --- RELEASE_NOTES.txt | 8 +++++++- pyotb/__init__.py | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 30ecf75..0ca0868 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,9 +1,15 @@ +--------------------------------------------------------------------- +2.0.2 (Mar 11, 2024) - Changes since version 2.0.1 + +- Fix auto install with OTB 9 +- Remove auto env setting functions since broken with OTB 9 +- Enhance logger config, removed pyotb.set_logger_level in favor of pyotb.logger.setLevel() + --------------------------------------------------------------------- 2.0.1 (Dec 18, 2023) - Changes since version 2.0.0 - Fix a bug when writing outputs in uint8 - --------------------------------------------------------------------- 2.0.0 (Nov 23, 2023) - Changes since version 1.5.4 diff --git a/pyotb/__init__.py b/pyotb/__init__.py index 9e49b6d..50173c0 100644 --- a/pyotb/__init__.py +++ b/pyotb/__init__.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """This module provides convenient python wrapping of otbApplications.""" -__version__ = "2.0.1" +__version__ = "2.0.2" from .install import install_otb -from .helpers import logger, set_logger_level +from .helpers import logger from .core import ( OTBObject, App, -- GitLab From 1d87290fa9deb4da72664577a61b0ee73cd8fca8 Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 16:02:51 +0100 Subject: [PATCH 6/9] STYLE: pylint unused import --- pyotb/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyotb/helpers.py b/pyotb/helpers.py index 829cb79..38a16e0 100644 --- a/pyotb/helpers.py +++ b/pyotb/helpers.py @@ -43,7 +43,7 @@ logging.config.dictConfig(logging_cfg) def try_import(): """Try to load OTB bindings or scan system, help user in case of failure.""" try: - import otbApplication as otb # pylint: disable=import-outside-toplevel + import otbApplication as otb # pylint: disable=import-outside-toplevel,unused-import except ModuleNotFoundError as e: if hasattr(sys, "ps1"): if input("Cannot import OTB. Do you want to install it ? (y/n): ") == "y": -- GitLab From 6600be011103a9c04e53435fd03ad10995439e18 Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 16:02:57 +0100 Subject: [PATCH 7/9] BUG: import logging.config --- pyotb/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyotb/helpers.py b/pyotb/helpers.py index 38a16e0..eaff829 100644 --- a/pyotb/helpers.py +++ b/pyotb/helpers.py @@ -1,6 +1,6 @@ """This module ensure we properly initialize pyotb, or raise SystemExit in case of broken install.""" -import logging +import logging.config import os import sys -- GitLab From 3e22fb8cedff88a724a36780ae49752cd5b0a12c Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Mon, 11 Mar 2024 16:03:46 +0100 Subject: [PATCH 8/9] CI: remove unused variables --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 57c9c42..1f989fc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -77,8 +77,6 @@ test_install: - "**/*.py" - .gitlab-ci.yml variables: - OTB_ROOT: /opt/otb - LD_LIBRARY_PATH: /opt/otb/lib SPOT_IMG_URL: https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/-/raw/develop/Data/Input/SP67_FR_subset_1.tif PLEIADES_IMG_URL: https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/-/raw/develop/Data/Baseline/OTB/Images/prTvOrthoRectification_pleiades-1_noDEM.tif before_script: -- GitLab From 66023d1a774f7e643a5fae87044cf639ffc620db Mon Sep 17 00:00:00 2001 From: Vincent Delbar <vincent.delbar@gmail.com> Date: Fri, 5 Apr 2024 14:54:35 +0200 Subject: [PATCH 9/9] Merge branch 'develop' --- .gitlab-ci.yml | 1 - RELEASE_NOTES.txt | 9 ++++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 262b7f9..715e8a3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -115,7 +115,6 @@ docs: stage: Documentation rules: - changes: - - "*.txt" - "*.md" - mkdocs.yml - doc/* diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 0ca0868..f764c2b 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,10 +1,17 @@ --------------------------------------------------------------------- -2.0.2 (Mar 11, 2024) - Changes since version 2.0.1 +2.0.3 (Apr 05, 2024) - Changes since version 2.0.2 - Fix auto install with OTB 9 - Remove auto env setting functions since broken with OTB 9 - Enhance logger config, removed pyotb.set_logger_level in favor of pyotb.logger.setLevel() +--------------------------------------------------------------------- +2.0.2 (Apr 5, 2024) - Changes since version 2.0.1 + +- Change docker image for testing to OTBTF +- Fix a bug with parameters of type "field" for vector files +- Fix wrong output parameter key in ImageClassifier and ImageClassifierFromDeepFeatures + --------------------------------------------------------------------- 2.0.1 (Dec 18, 2023) - Changes since version 2.0.0 -- GitLab